Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

Я думаю, что вы можете искать LINQ .Concat()?

var combined = foo.Concat(bar).Concat(foobar).Concat(...);

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

Ответ 2

Для меня 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. Это меньше проблем в С# 6. Вы можете просто написать:

return Concat(list1.Select(x = > x),
              list2.Where(x => true),
              list3.OrderBy(x => x));

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

list1 @ list2 // F#
list1 ++ list2 // Scala

Так чище.

Ответ 3

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

var combined = lists.Aggregate((acc, list) => { return acc.Concat(list); });

Ответ 4

Используйте Enumerable.Concat так:

var combined = foo.Concat(bar).Concat(baz)....;

Ответ 6

Вы всегда можете использовать 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();

Ответ 7

Единственное, что я вижу, это использовать 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() будет лучшим выбором, заключается в том, что это будет более очевидно для другого разработчика. Более читабельны и просты.

Ответ 8

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

var combined=foo.Union(bar).Union(baz)...

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

Ответ 9

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

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

Ответ 10

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

var combined = lists.Aggregate((l1, l2) => l1.Concat(l2));

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

Ответ 11

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

при условии, что эти списки:

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 ListExtensions
{
    public static void Add<T>(this List<T> list, IEnumerable<T> items) => list.AddRange(items);
}

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

var combined = new List<int> { list1, list2, 7, 8 };