I would like to create a function using Linq that summarizes an incoming sequence of values. The function should look something like this:
IDictionary<TKey, Summary<TKey>> Summarize<TKey, TValue>(IEnumerable<TValue> values)
{
return values
.ToLookup(val => GetKey(val)) // group values by key
.Union(*an empty grouping*) // make sure there is a default group
.ToDictionary(
group => group.Key,
group => CreateSummary(group)); // summarize each group
}
The catch is that the resulting IDictionary should have an entry for default(TKey) even if the incoming sequence contains no values with that key. Can this be done in a purely functional way? (Not using mutable data structures.)
The only way I can think to do it is by calling .Union on the lookup before piping it into a dictionary. But that would require me to create an empty IGrouping, which does not appear to be possible without an explicit class. Is there an elegant way to do this?
Edit: We can assume that TKey is a value type.
You can't get empty groups from GroupBy, nor from ToLookup. Perhaps there's an intentional reason.
Can this be done in a purely functional way? (Not using mutable data structures.)
While such academic requirements can be fun, any solution should be compared to the simplicity of a straight-forward implementation.
Dictionary<TKey, Summary<TKey>> result = values
.GroupBy(val => GetKey(val))
.ToDictionary(g => g.Key, g => CreateSummary(g));
TKey x = default(TKey);
if (!result.ContainsKey(x))
{
result[x] = CreateSummary(Enumerable.Empty<TValue>());
}
return result;
Now if you want an Empty Group, just have to add a class for it:
public class EmptyGroup<TKey, TValue> : IGrouping<TKey, TValue>
{
public TKey Key {get;set;}
public IEnumerator GetEnumerator()
{
return GetEnumerator<TValue>();
}
public IEnumerator<TValue> GetEnumerator<TValue>()
{
return Enumerable.Empty<TValue>().GetEnumerator<TValue>();
}
}
Used like this:
EmptyGroup<TKey, TValue> empty = new EmptyGroup<TKey, TValue>(Key = default<TKey>());