Скажем, у меня есть произвольное количество коллекций, каждая из которых содержит объекты одного типа (например, List<int> foo
и List<int> bar
). Если бы эти коллекции сами были в коллекции (например, типа List<List<int>>
, я мог бы использовать, SelectMany
чтобы объединить их все в одну коллекцию.
Однако, если эти коллекции еще не находятся в одной коллекции, у меня сложилось впечатление, что мне пришлось бы написать такой метод:
public static IEnumerable<T> Combine<T>(params ICollection<T>[] toCombine)
{
return toCombine.SelectMany(x => x);
}
Что я бы тогда назвал так:
var combined = Combine(foo, bar);
Есть ли чистый, элегантный способ комбинировать (любое количество) коллекций без необходимости писать служебный метод, как Combine
указано выше? Кажется достаточно простым, что должен быть способ сделать это в LINQ, но, возможно, нет.
c#
linq
generics
collections
Пончик
источник
источник
Ответы:
Я думаю, вы можете искать LINQ
.Concat()
?var combined = foo.Concat(bar).Concat(foobar).Concat(...);
В качестве альтернативы
.Union()
удалит повторяющиеся элементы.источник
.Union()
подсказку. Имейте в виду, что вам нужно будет реализовать IComparer в своем настраиваемом типе, если это так.Для меня
Concat
метод расширения не очень элегантен в моем коде, когда у меня есть несколько больших последовательностей для объединения. Это просто проблема с отступом / форматированием кода и что-то очень личное.Конечно, это хорошо выглядит так:
var list = list1.Concat(list2).Concat(list3);
Не так хорошо читается, когда читается так:
var list = list1.Select(x = > x) .Concat(list2.Where(x => true) .Concat(list3.OrderBy(x => x));
Или когда это выглядит так:
return Normalize(list1, a, b) .Concat(Normalize(list2, b, c)) .Concat(Normalize(list3, c, d));
или каково ваше предпочтительное форматирование. С более сложными конкатами дела обстоят хуже. Причина моего рода когнитивного диссонанса с вышеуказанным стилем является то , что первыми последовательность лежит за пределами
Concat
метода , тогда как последующие последовательности лежат внутри. Я предпочитаю вызывать статическийConcat
метод напрямую, а не стиль расширения:var list = Enumerable.Concat(list1.Select(x => x), list2.Where(x => true));
Для большего количества объединений последовательностей я использую тот же статический метод, что и в OP:
public static IEnumerable<T> Concat<T>(params IEnumerable<T>[] sequences) { return sequences.SelectMany(x => x); }
Итак, я могу написать:
return EnumerableEx.Concat ( list1.Select(x = > x), list2.Where(x => true), list3.OrderBy(x => x) );
Выглядит лучше. Дополнительное, иначе избыточное имя класса, которое я должен написать, не проблема для меня, учитывая, что мои последовательности выглядят чище с
Concat
вызовом. В C # 6 это не проблема . Вы можете просто написать:return Concat(list1.Select(x = > x), list2.Where(x => true), list3.OrderBy(x => x));
Хотелось бы, чтобы у нас были операторы конкатенации списков в C #, например:
list1 @ list2 // F# list1 ++ list2 // Scala
Так намного чище.
источник
Where
,OrderBy
) для ясности, особенно если они являются чем-то более сложным, чем этот пример.Для случая , когда вы делаете есть коллекция коллекций, то есть
List<List<T>>
,Enumerable.Aggregate
более элегантный способ объединить все списки в одном:var combined = lists.Aggregate((acc, list) => { return acc.Concat(list); });
источник
lists
сюда первым? Это первая проблема OP. Если бы у него былоcollection<collection>
начало, тогда было быSelectMany
намного проще.Используйте
Enumerable.Concat
так:var combined = foo.Concat(bar).Concat(baz)....;
источник
Использование
Enumerable.Concact
:var query = foo.Concat(bar);
источник
Вы всегда можете использовать Aggregate в сочетании с Concat ...
var listOfLists = new List<List<int>> { new List<int> {1, 2, 3, 4}, new List<int> {5, 6, 7, 8}, new List<int> {9, 10} }; IEnumerable<int> combined = new List<int>(); combined = listOfLists.Aggregate(combined, (current, list) => current.Concat(list)).ToList();
источник
SelectMany
просто попроще.Единственный способ, который я вижу, - это использовать
Concat()
var foo = new List<int> { 1, 2, 3 }; var bar = new List<int> { 4, 5, 6 }; var tor = new List<int> { 7, 8, 9 }; var result = foo.Concat(bar).Concat(tor);
Но стоит определиться, что лучше:
var result = Combine(foo, bar, tor);
или
var result = foo.Concat(bar).Concat(tor);
Одно из соображений, почему
Concat()
это будет лучший выбор, состоит в том, что он будет более очевидным для другого разработчика. Более читабельно и просто.источник
Вы можете использовать Union следующим образом:
var combined=foo.Union(bar).Union(baz)...
Тем не менее, это приведет к удалению идентичных элементов, поэтому, если они у вас есть, вы можете использовать
Concat
вместо них.источник
Enumerable.Union
представляет собой объединение множеств, которое не дает желаемого результата (оно дает дубликаты только один раз).int
в моем примере могло немного отпугнуть людей; ноUnion
в этом случае у меня не сработает.Пара методов с использованием инициализаторов коллекций -
предполагая эти списки:
var list1 = new List<int> { 1, 2, 3 }; var list2 = new List<int> { 4, 5, 6 };
SelectMany
с инициализатором массива (для меня не очень элегантно, но не полагается ни на какие вспомогательные функции):var combined = new []{ list1, list2 }.SelectMany(x => x);
Определите расширение списка для Add, которое позволяет
IEnumerable<T>
вList<T>
инициализаторе :public static class CollectionExtensions { public static void Add<T>(this ICollection<T> collection, IEnumerable<T> items) { foreach (var item in items) collection.Add(item); } }
Затем вы можете создать новый список, содержащий элементы других подобных (он также позволяет смешивать отдельные элементы).
var combined = new List<int> { list1, list2, 7, 8 };
источник
Учитывая, что вы начинаете с нескольких отдельных коллекций, я думаю, что ваше решение довольно элегантно. Вам нужно будет что- то сделать, чтобы соединить их вместе.
Было бы более удобным синтаксически сделать метод расширения из вашего метода Combine, который сделает его доступным везде, где бы вы ни находились.
источник
Все, что вам нужно, это для любого
IEnumerable<IEnumerable<T>> lists
:var combined = lists.Aggregate((l1, l2) => l1.Concat(l2));
Это объединит все элементы в
lists
одинIEnumerable<T>
(с дубликатами). ИспользуйтеUnion
вместоConcat
удаления дубликатов, как указано в других ответах.источник