结合多个元素集合的优雅方式?
Posted
技术标签:
【中文标题】结合多个元素集合的优雅方式?【英文标题】:Elegant way to combine multiple collections of elements? 【发布时间】:2011-05-28 11:44:44 【问题描述】:假设我有任意数量的集合,每个集合都包含相同类型的对象(例如,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 中应该有一种方法可以做到这一点,但也许没有。
【问题讨论】:
相关:listt-concatenation-for-x-amount-of-lists 当心 Concat 连接大量可枚举,可能会炸毁你的堆栈:programmaticallyspeaking.com/… 【参考方案1】:使用Enumerable.Concat
:
var query = foo.Concat(bar);
【讨论】:
【参考方案2】:使用集合初始化器的几种技术 --
假设这些列表:
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>
initializer 中:
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 ;
【讨论】:
【参考方案3】:如果您确实拥有一个集合集合,即List<List<T>>
,Enumerable.Aggregate
是将所有列表合并为一个的更优雅的方式:
var combined = lists.Aggregate((acc, list) => return acc.Concat(list); );
【讨论】:
您如何首先在此处获得lists
?这是 OP 的第一个问题。如果他有一个collection<collection>
开头,那么SelectMany
就简单多了。
不确定发生了什么,但这似乎回答了错误的问题。编辑答案以反映这一点。很多人似乎确实到了这里,因为需要结合 List>【参考方案4】:
对我来说,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
),尤其是当它们比这个例子更复杂时。 【参考方案5】:
对于任何IEnumerable<IEnumerable<T>> lists
,您只需要这个:
var combined = lists.Aggregate((l1, l2) => l1.Concat(l2));
这会将lists
中的所有项目合并为一个IEnumerable<T>
(有重复项)。如其他答案所述,使用 Union
而不是 Concat
删除重复项。
【讨论】:
【参考方案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();
【讨论】:
迄今为止最好的解决方案。 @OlegSelectMany
更简单。【参考方案7】:
我想您可能正在寻找 LINQ 的 .Concat()
?
var combined = foo.Concat(bar).Concat(foobar).Concat(...);
或者,.Union()
将删除重复元素。
【讨论】:
谢谢;我一开始没有这样做的唯一原因是(对我来说)你必须处理的收藏越多,它似乎就越难看。然而,这具有使用现有 LINQ 函数的好处,未来的开发人员可能已经熟悉这些函数。 感谢.Union()
提示。请记住,您将需要在您的自定义类型上实现 IComparer,以防万一。【参考方案8】:
鉴于您从一堆单独的集合开始,我认为您的解决方案相当优雅。您将不得不做一些事情将它们缝合在一起。
从您的 Combine 方法中创建一个扩展方法在语法上会更方便,这样您可以随时随地使用它。
【讨论】:
我喜欢扩展方法的想法,我只是不想重新发明***,如果 LINQ 已经有一个可以做同样事情的方法(并且可以立即被其他开发人员理解) )【参考方案9】:像这样使用Enumerable.Concat
:
var combined = foo.Concat(bar).Concat(baz)....;
【讨论】:
【参考方案10】:我看到的唯一方法是使用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()
会是一个更好的选择,这对于其他开发人员来说会更加明显。更具可读性和简单性。
【讨论】:
【参考方案11】:您可以按如下方式使用联合:
var combined=foo.Union(bar).Union(baz)...
不过,这将删除相同的元素,因此如果您有这些元素,您可能希望使用 Concat
来代替。
【讨论】:
不!Enumerable.Union
是一个集合并集,它不会产生所需的结果(它只会产生一次重复)。
是的,我需要对象的具体实例,因为我需要对它们进行一些特殊处理。在我的例子中使用int
可能会让人们有点失望;但Union
在这种情况下对我不起作用。以上是关于结合多个元素集合的优雅方式?的主要内容,如果未能解决你的问题,请参考以下文章