Katakanlah saya memiliki jumlah koleksi yang berubah-ubah, masing-masing berisi objek dengan jenis yang sama (misalnya, List<int> foo
dan List<int> bar
). Jika koleksi-koleksi ini adalah koleksi mereka sendiri (misalnya, dari tipe List<List<int>>
, saya dapat menggunakan SelectMany
untuk menggabungkan semuanya menjadi satu koleksi.
Namun, jika koleksi ini belum ada dalam koleksi yang sama, kesan saya bahwa saya harus menulis metode seperti ini:
public static IEnumerable<T> Combine<T>(params ICollection<T>[] toCombine)
{
return toCombine.SelectMany(x => x);
}
Yang kemudian saya sebut seperti ini:
var combined = Combine(foo, bar);
Adakah cara yang bersih dan elegan untuk menggabungkan (sejumlah) koleksi tanpa harus menulis metode utilitas seperti di Combine
atas? Tampaknya cukup sederhana sehingga harus ada cara untuk melakukannya di LINQ, tetapi mungkin tidak.
c#
linq
generics
collections
Donat
sumber
sumber
Jawaban:
Saya pikir Anda mungkin mencari LINQ
.Concat()
?var combined = foo.Concat(bar).Concat(foobar).Concat(...);
Atau,
.Union()
akan menghapus elemen duplikat.sumber
.Union()
tipnya. Ingatlah bahwa Anda perlu menerapkan IComparer pada jenis kustom Anda jika memang demikian.Bagi saya
Concat
sebagai metode ekstensi tidak terlalu elegan dalam kode saya ketika saya memiliki beberapa urutan besar untuk digabungkan. Ini hanyalah masalah lekukan / pemformatan kode dan sesuatu yang sangat pribadi.Tentu itu terlihat bagus seperti ini:
var list = list1.Concat(list2).Concat(list3);
Tidak begitu terbaca ketika berbunyi seperti:
var list = list1.Select(x = > x) .Concat(list2.Where(x => true) .Concat(list3.OrderBy(x => x));
Atau jika terlihat seperti:
return Normalize(list1, a, b) .Concat(Normalize(list2, b, c)) .Concat(Normalize(list3, c, d));
atau apapun format pilihan Anda. Segalanya menjadi lebih buruk dengan concat yang lebih kompleks. Alasan saya jenis disonansi kognitif dengan gaya di atas adalah bahwa urutan pertama berada di luar
Concat
metode sedangkan urutan berikutnya terletak di dalam. Saya lebih suka memanggilConcat
metode statis secara langsung dan bukan gaya ekstensi:var list = Enumerable.Concat(list1.Select(x => x), list2.Where(x => true));
Untuk lebih banyak jumlah rangkaian urutan, saya membawa metode statis yang sama seperti di OP:
public static IEnumerable<T> Concat<T>(params IEnumerable<T>[] sequences) { return sequences.SelectMany(x => x); }
Jadi saya bisa menulis:
return EnumerableEx.Concat ( list1.Select(x = > x), list2.Where(x => true), list3.OrderBy(x => x) );
Terlihat lebih baik. Nama kelas ekstra, jika tidak berlebihan, yang harus saya tulis bukan masalah bagi saya mengingat urutan saya terlihat lebih bersih dengan
Concat
panggilan tersebut. Ini bukan masalah di C # 6 . Anda tinggal menulis:return Concat(list1.Select(x = > x), list2.Where(x => true), list3.OrderBy(x => x));
Berharap kami memiliki daftar operator penggabungan di C #, seperti:
list1 @ list2 // F# list1 ++ list2 // Scala
Jauh lebih bersih.
sumber
Where
,OrderBy
) terlebih dahulu untuk kejelasan, terutama jika ada yang lebih kompleks daripada contoh ini.Untuk kasus ketika Anda memang memiliki koleksi koleksi, yaitu a
List<List<T>>
,Enumerable.Aggregate
adalah cara yang lebih elegan untuk menggabungkan semua daftar menjadi satu:var combined = lists.Aggregate((acc, list) => { return acc.Concat(list); });
sumber
lists
sini dulu? Itulah masalah pertama yang dimiliki OP. Jika dia haruscollection<collection>
memulainya, ituSelectMany
jauh lebih sederhana.Gunakan
Enumerable.Concat
seperti ini:var combined = foo.Concat(bar).Concat(baz)....;
sumber
Penggunaan
Enumerable.Concact
:var query = foo.Concat(bar);
sumber
Anda selalu bisa menggunakan Agregat yang dikombinasikan dengan 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();
sumber
SelectMany
lebih sederhana.Satu-satunya cara yang saya lihat adalah menggunakan
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);
Tetapi Anda harus memutuskan mana yang lebih baik:
var result = Combine(foo, bar, tor);
atau
var result = foo.Concat(bar).Concat(tor);
Satu poin mengapa
Concat()
akan menjadi pilihan yang lebih baik adalah bahwa itu akan lebih jelas bagi pengembang lain. Lebih mudah dibaca dan sederhana.sumber
Anda dapat menggunakan Union sebagai berikut:
var combined=foo.Union(bar).Union(baz)...
Ini akan menghapus elemen yang identik, jadi jika Anda memilikinya, Anda mungkin ingin menggunakannya
Concat
.sumber
Enumerable.Union
adalah gabungan himpunan yang tidak menghasilkan hasil yang diinginkan (hanya menghasilkan duplikat sekali).int
dalam contoh saya mungkin telah membuat orang sedikit kecewa; tetapiUnion
tidak akan berhasil untuk saya dalam kasus ini.Beberapa teknik menggunakan Collection Initializers -
dengan asumsi daftar ini:
var list1 = new List<int> { 1, 2, 3 }; var list2 = new List<int> { 4, 5, 6 };
SelectMany
dengan penginisialisasi array (tidak terlalu elegan bagi saya, tetapi tidak bergantung pada fungsi pembantu):var combined = new []{ list1, list2 }.SelectMany(x => x);
Tentukan ekstensi daftar untuk Tambah yang memungkinkan
IEnumerable<T>
dalamList<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); } }
Kemudian Anda dapat membuat daftar baru yang berisi elemen-elemen lain seperti ini (ini juga memungkinkan satu item untuk dicampur).
var combined = new List<int> { list1, list2, 7, 8 };
sumber
Mengingat Anda memulai dengan sekumpulan koleksi terpisah, saya pikir solusi Anda agak elegan. Anda harus melakukan sesuatu untuk menyatukannya.
Akan lebih mudah secara sintaksis untuk membuat metode ekstensi dari metode Gabungkan Anda, yang akan membuatnya tersedia di mana pun Anda pergi.
sumber
Yang Anda butuhkan hanyalah ini, untuk semua
IEnumerable<IEnumerable<T>> lists
:var combined = lists.Aggregate((l1, l2) => l1.Concat(l2));
Ini akan menggabungkan semua item
lists
menjadi satuIEnumerable<T>
(dengan duplikat). GunakanUnion
daripadaConcat
menghapus duplikat, seperti yang dicatat dalam jawaban lain.sumber