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


94

それぞれに同じタイプのオブジェクトを含む任意の数のコレクションがあるとします(たとえば、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でそれを行う方法があるべきであるほど十分に単純に思えますが、おそらくそうではありません。



あなたのスタックを爆破でき、enumerablesの非常に大きな数を連結するための連結の用心:programmaticallyspeaking.com/...
nawfal

回答:


107

LINQをお探し.Concat()ですか?

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

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


2
ありがとう。私が最初にこれを行わなかった唯一の理由は、(私にとって)対処しなければならないコレクションが増えるほど醜くなるように見えるからです。ただし、これには既存のLINQ関数を使用できるという利点があります。これは、将来の開発者がすでに慣れている可能性があります。
2010

2
.Union()先端をありがとう。クマの心にあなたが必要となることがある場合には、カスタムタイプにしたIComparerを実装します。
Gonzo345

29

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

その方がずっときれいです。


4
コーディングスタイルについてのあなたの懸念に感謝します。これははるかにエレガントです。
ブライアンレイナー

おそらく、あなたの回答のより小さなバージョンを、ここのトップの回答とマージすることができます。
ブライアンレイナー

2
この書式設定も好きです。ただし、特にこの例よりも複雑な場合は、明確にするために最初に個々のリスト操作(WhereなどOrderBy)を実行することを好みます。
brichins 2017年

27

あなたがケースのためにやるのコレクションのコレクションを持っている、すなわちAはList<List<T>>Enumerable.Aggregate一つにすべてのリストを結合するために、よりエレガントな方法です:

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

1
どのようにしてlists最初にここに来るのですか?それがOPの最初の問題です。そもそも彼がcollection<collection>最初に持っていたなら、それSelectManyはずっと簡単なことです。
nawfal

何が起こったのかわかりませんが、これは間違った質問に答えているようです。これを反映するように編集された回答。List <List <T >>を組み合わせる必要があるため、多くの人がここで終わるようです
astreltsov



7

あなたは常に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();

1
これまでのところ最高のソリューション。
Oleg

@Olegの方SelectManyが単純です。
nawfal

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() 1つは、他の開発者にとってより明白になるということです。より読みやすく、シンプルです。


3

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

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

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


4
番号!Enumerable.Union目的の結果を生成しないセットユニオンです(重複は1回しか生成されません)。
ジェイソン2010

ええ、オブジェクトに対して特別な処理を行う必要があるため、オブジェクトの特定のインスタンスが必要です。int私の例で使用すると、人々を少し放り出したかもしれません。しかし、Unionこの場合には私のために動作しません。
2010

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> ます

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

1

たくさんの別々のコレクションから始めていることを考えると、あなたのソリューションはかなりエレガントだと思います。あなたはそれらをつなぎ合わせるために何かをしなければならないでしょう。

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


私は拡張メソッドのアイデアが好きです。LINQに同じことをするメソッドがすでにある場合は、車輪を再発明したくありませんでした(そして、他の開発者がすぐに理解できるようになります)
Donut

1

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.