Linq:创建空的IGrouping

时间:2011-11-08 05:43:37

标签: linq igrouping ilookup

我想使用Linq创建一个函数,该函数汇总了传入的值序列。该函数应如下所示:

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
}

问题是得到的IDictionary应该有一个默认条目(TKey),即使传入的序列不包含该键的值。这可以用纯粹的功能方式完成吗? (不使用可变数据结构。)

我能想到的唯一方法就是在查找之前调用.Union,然后再将其放入字典中。但这需要我创建一个空的IGrouping,如果没有显式类,这似乎是不可能的。是否有一种优雅的方式来做到这一点?

编辑:我们可以假设TKey是一种值类型。

3 个答案:

答案 0 :(得分:3)

您不能从GroupBy或ToLookup获取空组。也许这是故意的原因。

  

这可以用纯粹的功能方式完成吗? (不使用可变数据结构。)

虽然这些学术要求很有趣,但任何解决方案都应与直接实施的简单性进行比较。

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;

现在,如果你想要一个空组,只需要为它添加一个类:

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>();
  }
}

像这样使用:

EmptyGroup<TKey, TValue> empty = new EmptyGroup<TKey, TValue>(Key = default<TKey>());

答案 1 :(得分:2)

接受的答案是我正在寻找的但它对我不起作用。也许我错过了一些东西,但它没有编译。 我不得不修改代码来修复它。 以下代码对我有用:

public class EmptyGroup<TKey, TValue> : IGrouping<TKey, TValue>
{
    public TKey Key { get; set; }

    public IEnumerator<TValue> GetEnumerator()
    {
        return Enumerable.Empty<TValue>().GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

像这样使用

var emptyGroup = new EmptyGroup<Customer, AccountingPaymentClient>();

答案 2 :(得分:0)

您可以添加第二个选项,其中您检查查找表是否包含任何条目,如果没有,则创建新的查找表。这与您提出的联合解决方案的不同之处在于,如果存在其他值,则不会添加默认值。

见:

IDictionary<TKey, Summary<TKey>> Summarize<TKey, TValue>(IEnumerable<TValue> values) 
{ 
    return values 
        .ToLookup(val => GetKey(val))         // group values by key 
        .Select(x => x.Any() ? x : Enumerable.Repeat(default(TKey), 1).ToLookup(x => GetKey(x)))
        .ToDictionary( 
            group => group.Key, 
            group => CreateSummary(group));   // summarize each group 
} 

如果您希望解决方案具有union,您可以使用相同的逻辑来创建默认查找表,如:

IDictionary<TKey, Summary<TKey>> Summarize<TKey, TValue>(IEnumerable<TValue> values) 
{ 
    return values 
        .ToLookup(val => GetKey(val))         // group values by key 
        .Union(Enumerable.Repeat(default(TKey), 1).ToLookup(x => GetKey(x)))
        .ToDictionary( 
            group => group.Key, 
            group => CreateSummary(group));   // summarize each group 
} 

希望这有帮助