Elegant way to combine multiple collections of elements?

前端 未结 11 2374
再見小時候
再見小時候 2020-12-05 03:36

Say I have an arbitrary number of collections, each containing objects of the same type (for example, List foo and List bar).

相关标签:
11条回答
  • 2020-12-05 04:12

    You can use Union as follows:

    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.

    0 讨论(0)
  • I think you might be looking for LINQ's .Concat()?

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

    Alternatively, .Union() will remove duplicate elements.

    0 讨论(0)
  • 2020-12-05 04:17

    To me Concat as an extension method is not very elegant in my code when I have multiple large sequences to concat. This is merely a codde indentation/formatting problem and something very personal.

    Sure it looks good like this:

    var list = list1.Concat(list2).Concat(list3);
    

    Not so readable when it reads like:

    var list = list1.Select(x = > x)
       .Concat(list2.Where(x => true)
       .Concat(list3.OrderBy(x => x));
    

    Or when it looks like:

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

    For more number of concats of sequences I carry the same static method as in OP:

    public static IEnumerable<T> Concat<T>(params IEnumerable<T>[] sequences)
    {
        return sequences.SelectMany(x => x);
    }
    

    So I can write:

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

    Looks better. The extra, otherwise redundant, class name I have to write is not a problem for me considering my sequences look cleaner with the Concat call. It's less of a problem in C# 6. You can just write:

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

    Wished we had list concatenation operators in C#, something like:

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

    Much cleaner that way.

    0 讨论(0)
  • 2020-12-05 04:25

    All you need is this, for any 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.

    0 讨论(0)
  • 2020-12-05 04:26

    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 something to stitch them together.

    It would be more convenient syntactically to make an extension method out of your Combine method, which would make it available anywhere you go.

    0 讨论(0)
  • 2020-12-05 04:27

    For the case when you do have a collection of collections, i.e. a List<List<T>>, Enumerable.Aggregate is a more elegant way to combine all lists into one:

    var combined = lists.Aggregate((acc, list) => { return acc.Concat(list); });
    
    0 讨论(0)
提交回复
热议问题