Как объединить две коллекции c
Перейти к содержимому

Как объединить две коллекции c

Элегантный способ объединить несколько коллекций элементов?

Скажем, у меня есть произвольное количество коллекций, каждая из которых содержит объекты одного типа (например, List<int> foo и List<int> bar ). Если бы эти коллекции сами были в коллекции (например, типа List<List<int>> , я мог бы использовать SelectMany , чтобы объединить их все в одну коллекцию.

Однако, если эти коллекции еще не находятся в одной коллекции, мне кажется, что мне придется написать такой метод:

Что я бы тогда назвал так:

Есть ли чистый, элегантный способ комбинировать (любое количество) коллекций без необходимости писать служебный метод, подобный Combine выше? Кажется достаточно простым, что должен быть способ сделать это в LINQ, но, возможно, нет.

11 ответов

В качестве альтернативы .Union() удалит повторяющиеся элементы.

Для меня Concat как метод расширения не очень элегантен в моем коде, когда мне нужно объединить несколько больших последовательностей. Это просто проблема с отступом / форматированием кода и что-то очень личное.

Конечно, это хорошо выглядит так:

Не так хорошо читается, когда он читается так:

Или когда это выглядит так:

Или любое другое предпочтительное форматирование. С более сложными конкатами дела обстоят хуже. Причина моего своего рода когнитивного диссонанса с вышеуказанным стилем заключается в том, что первая последовательность лежит вне метода Concat , тогда как последующие последовательности лежат внутри. Я предпочитаю вызывать статический метод Concat напрямую, а не стиль расширения:

Для большего количества объединений последовательностей я использую тот же статический метод, что и в OP:

Итак, я могу написать:

Выглядит лучше. Дополнительное, иначе повторяющееся имя класса, которое я должен написать, не проблема для меня, учитывая, что мои последовательности выглядят чище с вызовом Concat . В C # 6 это меньше проблем. Вы можете просто написать:

Хотелось бы, чтобы у нас были операторы конкатенации списков в C #, например:

Так намного чище.

В случае, когда у вас действительно есть коллекция коллекций, то есть List<List<T>> , Enumerable.Aggregate — более элегантный способ объединить все списки в один:

Вы всегда можете использовать Aggregate в сочетании с Concat .

Единственный способ, который я вижу, — это использовать Concat()

Но стоит определиться, что лучше:

Один момент, почему Concat() будет лучшим выбором , заключается в том, что это будет более очевидным для другого разработчика. Более читабельно и просто.

Вы можете использовать Union следующим образом:

Однако это удалит идентичные элементы, поэтому, если они у вас есть, вы можете вместо этого использовать Concat .

Пара методов с использованием инициализаторов коллекций —

Предполагая эти списки:

SelectMany с инициализатором массива (для меня это не очень элегантно, но не полагается на какие-либо вспомогательные функции):

Определите расширение списка для добавления, которое позволяет IEnumerable<T> в List<T> initializer:

Затем вы можете создать новый список, содержащий элементы других подобных (он также позволяет смешивать отдельные элементы).

Учитывая, что вы начинаете с нескольких отдельных коллекций, я думаю, что ваше решение довольно элегантно. Вам нужно будет сделать что-нибудь , чтобы соединить их вместе.

Синтаксически было бы удобнее сделать метод расширения из вашего метода Combine, который сделает его доступным везде, где бы вы ни находились.

Все, что вам нужно, это для любого IEnumerable<IEnumerable<T>> lists :

Это объединит все элементы в lists в один IEnumerable<T> (с дубликатами). Используйте Union вместо Concat для удаления дубликатов, как указано в других ответах.

Операции Distinct, Union, Except и Intersect

XYZ School

Операции множеств используются для выполнения математических операций с множествами на последовательностях.

Distinct

Операция Distinct удаляет дублированные элементы из входной последовательности. У операции Distinct есть один прототип, описанный ниже:

Эта операция возвращает объект, перечисляющий элементы входной последовательности source и выдающий последовательность, в которой каждый элемент не эквивалентен предыдущим выданным. Эквивалентность элементов определяется методами GetHashCode и Equals.

Давайте рассмотрим пример использования операции Distinct:

Использование LINQ-операции Distinct

Union

Операция Union возвращает объединение множеств из двух исходных последовательностей. У этой операции имеется один прототип, описанный ниже:

Эта операция возвращает объект, который сначала перечисляет элементы последовательности по имени first, выдавая последовательность, в которой каждый элемент не эквивалентен предыдущим выданным, затем перечисляет вторую входную последовательность second, опять-таки, выдавая последовательность без повторений. Эквивалентность элементов определяется методами GetHashCode и Equals.

Чтобы продемонстрировать разницу между операцией Union и описанной ранее операцией Concat, в примере, представленном ниже, создаются последовательности first и second из массива cars, что приведет к дублированию пятого элемента в обеих последовательностях. Затем отображается количество элементов в массиве cars, а также в последовательностях first и second, наряду с количеством элементов в конкатенированной и объединенной последовательностях:

В конечном итоге последовательность concat должна иметь на один элемент больше, чем массив cars. Последовательность union должна содержать то же количество элементов, что и массив cars. Это доказывают результаты выполнения кода:

Пример вызова LINQ-операции Union

Intersect

Операция Intersect возвращает пересечение множеств из двух исходных последовательностей. Операция Intersect имеет один прототип, описанный ниже:

Эта операция возвращает объект, который сначала перечисляет элементы последовательности по имени first, выбирая оттуда каждый элемент, который не эквивалентен предыдущему выбранному элементу. Затем он перечисляет вторую входную последовательность, помечая любой элемент, имеющийся в обеих последовательностях, для включения в выходную последовательность. Затем осуществляется проход по помеченным элементам, с помещением их в выходную последовательность в том порядке, в котором они были собраны. Эквивалентность элементов определяется с помощью методов GetHashCode и Equals.

Чтобы продемонстрировать применение операции Intersect, ниже используются операции Take и Skip для генерации двух последовательностей и получения некоторого их перекрытия, как в примере с Union, где был намеренно дублирован пятый элемент. После вызова операции Intersect на этих двух сгенерированных последовательностях в возвращаемой последовательности intersect должен оказаться только дублированный пятый элемент:

В конечном итоге должна быть получена последовательность auto, которая состоит только из одного элемента, содержащего дублированный пятый элемент массива cars — Chevrolet:

Пример использования операции Intersect

Итак, LINQ работает! Насколько часто вам приходилось ранее выполнять операции над множествами элементов из двух коллекций? Было ли это трудно? Благодаря LINQ, все сложности в прошлом.

Except

Операция Except возвращает последовательность, содержащую все элементы первой последовательности, которых нет во второй последовательности. Эта операция имеет один прототип, описанный ниже:

Эта операция возвращает объект, который при перечислении перебирает элементы входной последовательности по имени second, собирая все элементы, которые не эквивалентны ранее собранным. Затем происходит перечисление входной последовательности first, с выдачей каждого ее элемента, которого нет в коллекции из второй последовательности. Эквивалентность одного элемента другому определяется с использованием их методов GetHashCode и Equals.

В следующем примере используются два массива arr1 и arr2. С помощью операции Except будут удалены взаимоисключающие элементы коллекций:

Как объединить две коллекции c

LINQ предоставляет несколько методов для работы с коллекциями как с множествами, а именно находить их разность, объединение и пересечение.

Разность последовательностей

С помощью метода Except() можно получить разность двух последовательностей:

В данном случае из массива soft убираются все элементы, которые есть в массиве hard . Результатом операции будут два элемента:

Пересечение последовательностей

Для получения пересечения последовательностей, то есть общих для обоих наборов элементов, применяется метод Intersect :

Так как оба набора имеют только один общий элемент, то соответственно только он и попадет в результирующую выборку:

Удаление дубликатов

Для удаления дублей в наборе используется метод Distinct :

Объединение последовательностей

Для объединения двух последовательностей используется метод Union . Его результатом является новый набор, в котором имеются элементы, как из первой, так и из второй последовательности. Повторяющиеся элементы добавляются в результат только один раз:

Результатом операции будет следующий набор:

Если же нам нужно простое объединение двух наборов, то мы можем использовать метод Concat :

В этом случае те элементы, которые встречаются в обоих наборах, дублируются в резутирующей последовательности.

Последовательное применение методов Concat и Distinct будет подобно действию метода Union.

Работа со сложными объектами

Для сравнения объектов в последовательностях применяются реализации методов GetHeshCode() и Equals(). Поэтому если мы хотим работать с последовательностями, которые содержат объекты своих классов и структур, то нам необходимо определить для них подобные методы:

Здесь объекты Person сравниваются исходя из значения их свойства Name — если имена равны, то и объекты Person равны. Консольный вывод:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *