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

Posted

技术标签:

【中文标题】结合多个元素集合的优雅方式?【英文标题】:Elegant way to combine multiple collections of elements? 【发布时间】:2011-05-28 11:44:44 【问题描述】:

假设我有任意数量的集合,每个集合都包含相同类型的对象(例如,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 中应该有一种方法可以做到这一点,但也许没有。

【问题讨论】:

相关: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&lt;T&gt;List&lt;T&gt; 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&lt;List&lt;T&gt;&gt;Enumerable.Aggregate 是将所有列表合并为一个的更优雅的方式:

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

【讨论】:

您如何首先在此处获得lists?这是 OP 的第一个问题。如果他有一个collection&lt;collection&gt; 开头,那么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

这样更干净。

【讨论】:

感谢您对编码风格的关注。这更优雅。 也许您的答案的较小版本可以与此处的最佳答案合并。 我也喜欢这种格式——尽管为了清晰起见,我更喜欢先执行单个列表操作(例如WhereOrderBy),尤其是当它们比这个例子更复杂时。 【参考方案5】:

对于任何IEnumerable&lt;IEnumerable&lt;T&gt;&gt; lists,您只需要这个:

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

这会将lists 中的所有项目合并为一个IEnumerable&lt;T&gt;(有重复项)。如其他答案所述,使用 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();

【讨论】:

迄今为止最好的解决方案。 @Oleg SelectMany 更简单。【参考方案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 在这种情况下对我不起作用。

以上是关于结合多个元素集合的优雅方式?的主要内容,如果未能解决你的问题,请参考以下文章

你还在遍历搜索集合?别逗了,Java 8 一行代码足矣,是真的优雅

Java循环删除集合多个元素的正确打开方式

一个简单的Java集合范围过滤的多个方式对比

MATLAB如何更优雅的统计集合中各元素出现的次数?

初识java集合——队列

详解遍历集合和遍历集合时删除集合元素