web-dev-qa-db-ja.com

要素の複数のコレクションを組み合わせるエレガントな方法?

任意の数のコレクションがあり、それぞれに同じタイプのオブジェクトが含まれているとします(たとえば、List<int> fooおよびList<int> bar)。これらのコレクション自体がコレクションに含まれていた場合(例:タイプList<List<int>>SelectManyを使用して、それらをすべて1つのコレクションに結合できます。

ただし、これらのコレクションがまだ同じコレクションにない場合は、次のようなメソッドを作成する必要があると思います。

public static IEnumerable<T> Combine<T>(params ICollection<T>[] toCombine)
{
   return toCombine.SelectMany(x => x);
}

それを次のように呼び出します:

var combined = Combine(foo, bar);

上記のCombineのようなユーティリティメソッドを記述することなく、(任意の数の)コレクションを結合するためのクリーンでエレガントな方法はありますか? LINQでそれを行う方法があるはずですが、おそらくそうではないはずです。

76
Donut

LINQの .Concat() ?をお探しかもしれません。

_var combined = foo.Concat(bar).Concat(foobar).Concat(...);
_

または、.Union()は重複する要素を削除します。

85
Domenic

連結する複数の大きなシーケンスがある場合、拡張メソッドとしての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));

またはあなたの好みのフォーマットは何でも。複雑な連結により事態は悪化します。上記のスタイルと私のsortの認知的不協和の理由は、最初のシーケンスが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

そのようにはるかにクリーナー。

24
nawfal

doコレクションのコレクションがある場合、つまりList<List<T>>Enumerable.Aggregateは、すべてのリストを1つにまとめるよりエレガントな方法です。

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

つかいます - Enumerable.Concat のように:

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

つかいます - Enumerable.Concact

var query = foo.Concat(bar);
7
Ahmad Mageed

Concatと組み合わせたAggregateを常に使用できます...

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

私が見る唯一の方法は、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);
_

1つのポイントなぜConcat()がより良い選択になるは、別の開発者にとってより明白になるということです。より読みやすく、シンプル。

6
Restuta

Unionは次のように使用できます。

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

ただし、これにより同一の要素が削除されるため、それらがある場合は、代わりにConcatを使用することをお勧めします。

2

多数の個別のコレクションから始めることを考えると、あなたのソリューションはかなりエレガントだと思います。それらをつなぎ合わせるにはsomethingを実行する必要があります。

Combineメソッドから拡張メソッドを作成すると、構文上より便利になり、どこにいても使用できるようになります。

1
Dave Swersky

必要なのは、これだけですIEnumerable<IEnumerable<T>> lists

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

これにより、listsのすべてのアイテムが1つのIEnumerable<T>(重複あり)。他の回答に記載されているように、Unionの代わりにConcatを使用して重複を削除します。

0
Nir Maoz