5

我想使用 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 是一个值类型。

4

3 回答 3

5

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

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>();
于 2014-01-15T17:33:47.363 回答
4

您无法从 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>());
于 2011-11-08T16:07:19.030 回答
2

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

看:

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 
} 

希望这可以帮助

于 2011-11-08T06:18:12.323 回答