Late to the party but I think this should work, without needing to enumerate everything again and create temporary tuples/anonymous types.
public static ILookup<TKey, TElement> ToLookup<TKey, TElement>(
this IEnumerable<TKey> keys,
Func<TKey, IEnumerable<TElement>> selector)
{
return new ManualLookup<TKey, TElement>(keys, selector);
}
private class ManualLookup<TKey, TElement> : ILookup<TKey, TElement>
{
private IEnumerable<TKey> _keys;
private Func<TKey, IEnumerable<TElement>> _selector;
public ManualLookup(IEnumerable<TKey> keys, Func<TKey, IEnumerable<TElement>> selector)
{
_keys = keys;
_selector = selector;
}
public IEnumerable<TElement> this[TKey key] => _selector(key);
public int Count => _keys.Count();
public bool Contains(TKey key) => _keys.Contains(key);
public IEnumerator<IGrouping<TKey, TElement>> GetEnumerator() => _keys
.Select(key => new ManualGrouping<TKey, TElement>(key, _selector(key)))
.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
private class ManualGrouping<TKey, TElement> : IGrouping<TKey, TElement>
{
private TKey _key;
private IEnumerable<TElement> _enumerable;
public ManualGrouping(TKey key, IEnumerable<TElement> enumerable)
{
_key = key;
_enumerable = enumerable;
}
public TKey Key => _key;
public IEnumerator<TElement> GetEnumerator() => _enumerable.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
Then you can do something like:
Dictionary<MyType, List<MyOtherType>> dictionary;
return dictionary.Keys.ToLookup(key =>
{
if (dictionary.TryGetValue(key, out var list)
{
return list;
}
return Enumerable.Empty<MyOtherType>();
});