Elegant way to combine multiple collections of elements?

假设我有任意数量的集合,每个集合包含相同类型的对象(例如,List<int> fooList<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 中实现,但也许并非如此。

124932 次浏览

您可以按以下方式使用 Union:

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

This will remove identical elements, though, so if you have those, you might want to use Concat, instead.

使用 Enumerable.Concat:

var query = foo.Concat(bar);

我想你可能在找 LINQ 的 .Concat()

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

Alternatively, .Union() will remove duplicate elements.

我认为唯一的方法是使用 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);

why Concat() will be a better choice的一点是,它对于其他开发人员来说将更加明显,更加易读和简单。

像下面这样使用 Enumerable.Concat:

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

Given that you're starting with a bunch of separate collections, I think your solution is rather elegant. You're going to have to do 什么的 to stitch them together.

从语法上来说,将一个扩展方法从您的组合方法中移除会更加方便,这将使它可以在任何地方使用。

对我来说,当我有多个大序列要连接时,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));

or whatever your preferred formatting is. Things get worse with more complex concats. The reason for my 算是吧 cognitive dissonance with the above style is that the first sequence lie outside of the Concat method whereas the subsequent sequences lie inside. I rather prefer to call the static Concat method directly and not the extension style:

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

Much cleaner that way.

你总是可以使用聚合结合 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();

对于 有一个集合集合的情况,也就是 List<List<T>>Enumerable.Aggregate是将所有列表合并为一个列表的更优雅的方法:

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

所有你需要的是这个,任何 IEnumerable<IEnumerable<T>> lists:

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

This will combine all the items in lists into one IEnumerable<T> (with duplicates). Use Union instead of Concat to remove duplicates, as noted in the other answers.

A couple techniques using Collection Initializers --

假设这些名单:

var list1 = new List<int> { 1, 2, 3 };
var list2 = new List<int> { 4, 5, 6 };

带有数组初始值设定项的 SelectMany(对我来说并不那么优雅,但并不依赖于任何 helper 函数) :

var combined = new []{ list1, list2 }.SelectMany(x => x);

为 Add 定义一个扩展列表,它允许 List<T> 初始化程序中的 IEnumerable<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);
}
}

Then you can create a new list containing the elements of the others like this (it also allows single items to be mixed in).

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