【问题标题】:Linq: Create empty IGroupingLinq:创建空的 IGrouping
【发布时间】:2011-11-08 05:43:36
【问题描述】:

我想使用 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 也应该有一个 default(TKey) 条目。这可以以纯粹的功能方式完成吗? (不使用可变数据结构。)

我能想到的唯一方法是在查找时调用 .Union,然后再将其输入字典。但这需要我创建一个空的 IGrouping,如果没有明确的类,这似乎是不可能的。有没有优雅的方法来做到这一点?

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

【问题讨论】:

    标签: linq igrouping ilookup


    【解决方案1】:

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

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

    【讨论】:

    • 谢谢!这非常适合单元测试以覆盖必须从空组中调用 Sum 的代码分支。
    【解决方案2】:

    您无法从 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>());
    

    【讨论】:

      【解决方案3】:

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

      见:

      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 
      } 
      

      如果你想要一个联合的解决方案,你可以使用相同的逻辑来创建一个默认的查找表,如:

      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 
      } 
      

      希望对你有帮助

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-04-08
        • 2023-03-22
        • 1970-01-01
        • 2015-06-26
        • 2011-07-01
        • 1970-01-01
        • 2010-12-07
        相关资源
        最近更新 更多