2016-12-06 3 views
1

У меня есть результаты двух GroupBy:Linq Группа: результаты нескольких GroupBy

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

Я хочу, чтобы положить их вместе в одну последовательность. Некоторые элементы из результата2 могут иметь 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, который будет создавать объект Lookup так же, как перечисляется GroupedEnumerable.

Должен ли я сделать что-то подобное, или есть более приятный (и более простой для понимания) метод?

ответ

1

Получение желаемого результата относительно легко с использованием Concat, за которым следует GroupBy, и выравнивание каждой результирующей группировки с помощью SelectMany.

Проблема заключается в том, чтобы превратить его в IGrouping<TKey, TElement>, потому что нет общественного класса стандарта реализует этот интерфейс, не возвращенный GroupBy и ToLookup реализации, а также нет 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

Этот метод работает. Еще быстрее, чем метод, который я использовал (создайте словарь и либо добавьте каждую не существующую группу, либо добавьте существующую группу. Не уверен, хотя какое преимущество имеет ваша статическая функция Create, вместо того, чтобы просто использовать конструктор –

+0

Привет Харальд, это просто для удобства, похожее на 'Tuple.Create', чтобы компилятор выводил общие аргументы. Конечно, вы можете напрямую использовать конструктор, если хотите. –