2016-12-06 29 views
1

的我有两个的GroupBy结果的结果:LINQ的:集团若干的GroupBy

IEnumerable<IGrouping<long, MyClass>> result1 = ...; 
IEnumerable<IGrouping<long, MyClass>> result2 = ...; 

我希望把它们连成一个序列。 result2中的某些元素可能具有IGrouping键,这些键也是result1中的键。因此,标准的Enumerable.Concat不起作用:

IEnumerable<IGrouping<long, MyClass>> result = result1.Concat(result2); 

使用相同的密钥中的项目的结果序列两次提到。显然,我需要一个特殊的Concat用于IGrouping<TKey, TSource>这样的:

public static IEnumerable<IGrouping<TKey, TSource>> Concat<TSource, TKey> 
    (this IEnumerable<IGrouping<TKey, TSource>> first, 
      IEnumerable<IGrouping<TKey, TSource>> second) 

我可以再次取消对组中的所有元素,并将它们组合,但是这当然是一种浪费。我看过source code of Enumerable.GroupBy。该方法创建了一个GroupedEnumerable对象,该对象将像列举的GroupedEnumerable一样创建一个Lookup对象。

我应该做一些类似的事情,还是有更好的(更容易掌握)的方法?

回答

1

通过使用Concat然后GroupBy并使用SelectMany平滑每个得到的分组,获得期望的结果相对容易。

的问题是如何把它变成IGrouping<TKey, TElement>因为没有实现该接口的公共标准类,它是由GroupByToLookup实现返回,也没有GroupBy过载,使我们所需要的。因此,我们需要使我们自己的实现,幸运很简单:

class Grouping<TKey, TElement> : IGrouping<TKey, TElement> 
{ 
    IEnumerable<TElement> elements; 
    public Grouping(TKey key, IEnumerable<TElement> elements) 
    { 
     this.Key = key; 
     this.elements = elements; 
    } 
    public TKey Key { get; } 
    public IEnumerator<TElement> GetEnumerator() => elements.GetEnumerator(); 
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 
} 

public static class Grouping 
{ 
    public static IGrouping<TKey, TElement> Create<TKey, TElement>(TKey key, IEnumerable<TElement> elements) => 
     new Grouping<TKey, TElement>(key, elements); 
} 

现在所讨论的方法的实现可能是这样的:

return first.Concat(second) 
    .GroupBy(g => g.Key, (key, gg) => Grouping.Create(key, gg.SelectMany(g => g))); 
+0

此方法效果。甚至更快的方法,我使用的方法(创建一个字典并添加每个不存在的组,或addRange到现有的组。不知道你的静态Create函数的优点是什么,而不是只使用构造函数 –

+0

Hi Harald,这只是为了方便,类似于'Tuple.Create',以便让编译器推断泛型参数。当然,如果您愿意,您可以直接使用构造函数。 –