优雅的方式结合多个元素集合?

时间:2010-12-20 20:54:45

标签: c# linq generics collections

假设我有任意数量的集合,每个集合包含相同类型的对象(例如,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中完成它,但也许不是。

11 个答案:

答案 0 :(得分:81)

我想您可能正在寻找LINQ的.Concat()

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

或者,.Union()将删除重复的元素。

答案 1 :(得分:21)

对我来说Concat作为扩展方法在我的代码中不是很优雅,因为我有多个大序列要连接。这只是一个codde缩进/格式化问题,而且非常个人化。

当然看起来很不错:

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));

或任何您喜欢的格式。随着更复杂的concats,事情变得更糟。我的认知失调与上述风格的原因是第一个序列位于Concat方法之外,而后续序列位于其中。我更喜欢直接调用静态Concat方法而不是扩展样式:

var list = Enumerable.Concat(list1.Select(x => x),
                             list2.Where(x => true));

对于更多序列的concats,我使用与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

那种方式更干净。

答案 2 :(得分:20)

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

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

答案 3 :(得分:14)

像这样使用Enumerable.Concat

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

答案 4 :(得分:7)

使用Enumerable.Concact

var query = foo.Concat(bar);

答案 5 :(得分:7)

您总是可以将Aggregate与Concat结合使用......

-9

答案 6 :(得分:6)

我看到的唯一方法是使用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()将是一个更好的选择,对于其他开发者来说更​​明显。更具可读性和简单性。

答案 7 :(得分:2)

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

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

这会删除相同的元素,所以如果你有这些元素,你可能想要使用Concat

答案 8 :(得分:1)

鉴于您从一堆独立的系列开始,我认为您的解决方案相当优雅。你将不得不做某事来将它们拼接在一起。

使用Combine方法制作扩展方法在语法上会更方便,这样可以随时随地使用。

答案 9 :(得分:1)

使用集合初始化程序的几种技术-

假设这些列表:

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);

为“添加”定义列表扩展名,该扩展名允许IEnumerable<T> initializer中的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 };

答案 10 :(得分:0)

对于任何IEnumerable<IEnumerable<T>> lists

,您只需要这样做
var combined = lists.Aggregate((l1, l2) => l1.Concat(l2));

这会将lists中的所有项目合并为一个IEnumerable<T>(带有重复项)。使用Union代替Concat删除重复项,如其他答案中所述。