简体   繁体   English

Linq:创建空的IGrouping

[英]Linq: Create empty IGrouping

I would like to create a function using Linq that summarizes an incoming sequence of values. 我想使用Linq创建一个函数,它总结了一个传入的值序列。 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. 问题是生成的IDictionary应该有一个默认条目(TKey),即使传入的序列不包含该键的值。 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. 我能想到的唯一方法就是在查找之前调用.Union,然后再将其放入字典中。 But that would require me to create an empty IGrouping, which does not appear to be possible without an explicit class. 但这需要我创建一个空的IGrouping,如果没有显式类,这似乎是不可能的。 Is there an elegant way to do this? 有一种优雅的方式来做到这一点?

Edit: We can assume that TKey is a value type. 编辑:我们可以假设TKey是一种值类型。

You can't get empty groups from GroupBy, nor from ToLookup. 您无法从GroupBy或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>());

The accepted answer is what I was looking for but it didn't work for me. 接受的答案是我正在寻找的,但它对我不起作用。 Maybe I missed something but it didn't compile. 也许我错过了一些东西,但它没有编译。 I had to modify the code to fix it. 我不得不修改代码来修复它。 Here is the code that worked for me: 这是适合我的代码:

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

used like this 像这样用过

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

You can add a second select in which you check the lookup table has any entries and if not, create a new lookup table. 您可以添加第二个选择,其中您检查查找表是否包含任何条目,如果没有,则创建新的查找表。 This is different from your proposed union solution in that this doesn't add the default value IF there are other values. 这与您提出的联合解决方案的不同之处在于,如果有其他值,则不会添加默认值。

see: 看到:

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 
} 

If you want the solution with an union, you can use the same logic to create a default lookup table like: 如果您希望解决方案具有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 
} 

Hope this helps 希望这可以帮助

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM