Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Single extension method on IDictionary<K, IEnumerable/IList/ICollection<V>>

I'm trying to write an extension method that will convert IDictionary<K, S<V>> holding any type of collection/sequence (S<V>) to ILookup<K, V> which is more proper data structure in those cases. This means I'd like my extension to work on different S types and interfaces:

  • IDictionary<K, IEnumerable<V>>
  • IDictionary<K, ICollection<V>>
  • IDictionary<K, List<V>>

etc. Ideally, I don't want to write separate implementation for each possible collection type AND I want type inference to do its job.

What I've tried is:

public static ILookup<TKey, TValue>ToLookup<TKey, TCollection, TValue>(
    this IDictionary<TKey, TCollection> dictionary)
        where TCollection : IEnumerable<TValue>

But it have no TValue in parameters list, so type inference is unable to figure it out - I get "The type arguments for method ToLookup cannot be inferred from the usage".

Is there a chance it could work somehow in other way than adding fake TValue-typed parameter to the method?

Examples of expected usage

I hope all above calls to be possible and result in a call to my single extension method:

var dictOfIEnumerables = new Dictionary<int, IEnumerable<int>>();
var lookupFromIEnumerables = dictOfIEnumerables.ToLookup();

var dictOfICollections = new Dictionary<int, ICollection<int>>();
var lookupFromICollections = dictOfICollections.ToLookup();

var dictOfLists = new Dictionary<int, List<int>>();
var lookupFromLists = dictOfLists.ToLookup();
like image 740
NOtherDev Avatar asked Nov 12 '22 02:11

NOtherDev


1 Answers

Because all collections implement IEnumerable<T>, we can just use it instead of the TCollection type paramter. Unfortunately the type inference does not know this. This is the code I wrote:

public static ILookup<TKey, TValue> ToLookup<TKey, TValue>
        (this IDictionary<TKey, IEnumerable<TValue>> dict)
{
    return dict.SelectMany(p => p.Value.Select
        (v => new KeyValuePair<TKey, TValue>(p.Key, v)))
        .ToLookup(p => p.Key, p => p.Value);
}

There seems to be no way of making the type inference work, but this method will work if you cast the Dictionary:

((IDictionary<int, IEnumerable<int>>)dictOfLists).ToLookup()

Also you can add Lists to a Dictionary of IEnumerables and cast them back when you need them.

like image 80
timedt Avatar answered Nov 14 '22 23:11

timedt