Powiedzmy, że mam dowolną liczbę kolekcji, z których każda zawiera obiekty tego samego typu (na przykład List<int> foo
i List<int> bar
). Gdyby te kolekcje same znajdowały się w kolekcji (np. Czcionek List<List<int>>
, mógłbym użyć ich SelectMany
do połączenia ich wszystkich w jedną kolekcję).
Jeśli jednak te kolekcje nie są już w tej samej kolekcji, to mam wrażenie, że musiałbym napisać taką metodę:
public static IEnumerable<T> Combine<T>(params ICollection<T>[] toCombine)
{
return toCombine.SelectMany(x => x);
}
Które wtedy nazwałbym tak:
var combined = Combine(foo, bar);
Czy istnieje czysty, elegancki sposób łączenia (dowolnej liczby) kolekcji bez konieczności pisania metody użytkowej takiej jak Combine
powyżej? Wydaje się dość proste, że powinien istnieć sposób, aby to zrobić w LINQ, ale być może nie.
c#
linq
generics
collections
Pączek
źródło
źródło
Odpowiedzi:
Myślę, że możesz szukać LINQ
.Concat()
?var combined = foo.Concat(bar).Concat(foobar).Concat(...);
Alternatywnie,
.Union()
usunie zduplikowane elementy.źródło
.Union()
cynk. Pamiętaj, że jeśli tak jest, musisz zaimplementować IComparer na swoim typie niestandardowym.Dla mnie
Concat
metoda rozszerzająca nie jest zbyt elegancka w moim kodzie, gdy mam wiele dużych sekwencji do połączenia. To tylko problem z wcięciami / formatowaniem kodu i coś bardzo osobistego.Jasne, że wygląda dobrze tak:
var list = list1.Concat(list2).Concat(list3);
Nie tak czytelne, gdy brzmi:
var list = list1.Select(x = > x) .Concat(list2.Where(x => true) .Concat(list3.OrderBy(x => x));
Lub gdy wygląda na to:
return Normalize(list1, a, b) .Concat(Normalize(list2, b, c)) .Concat(Normalize(list3, c, d));
lub cokolwiek preferujesz. Sytuacja pogarsza się przy bardziej złożonych konkatacjach. Przyczyną mojego rodzaju dysonansu poznawczego z powyższym stylem jest to, że pierwsza sekwencja znajduje się poza
Concat
metodą, a kolejne sekwencje wewnątrz. Wolę bezpośrednio wywoływaćConcat
metodę statyczną , a nie styl rozszerzenia:var list = Enumerable.Concat(list1.Select(x => x), list2.Where(x => true));
Dla większej liczby konkatacji sekwencji stosuję tę samą metodę statyczną co w OP:
public static IEnumerable<T> Concat<T>(params IEnumerable<T>[] sequences) { return sequences.SelectMany(x => x); }
Mogę więc napisać:
return EnumerableEx.Concat ( list1.Select(x = > x), list2.Where(x => true), list3.OrderBy(x => x) );
Wygląda lepiej. Dodatkowa, w przeciwnym razie zbędna nazwa klasy, którą muszę napisać, nie stanowi dla mnie problemu, biorąc pod uwagę, że moje sekwencje wyglądają bardziej czysto po
Concat
wywołaniu. To mniejszy problem w C # 6 . Możesz po prostu napisać:return Concat(list1.Select(x = > x), list2.Where(x => true), list3.OrderBy(x => x));
Szkoda, że nie mamy operatorów konkatenacji list w C #, coś takiego:
list1 @ list2 // F# list1 ++ list2 // Scala
W ten sposób dużo czystszy.
źródło
Where
,OrderBy
) po raz pierwszy dla jasności, zwłaszcza jeśli są one coś bardziej skomplikowane niż w tym przykładzie.W przypadku, gdy robią mieć kolekcję zbiorów, czyli
List<List<T>>
,Enumerable.Aggregate
jest bardziej elegancki sposób, aby połączyć wszystkie listy w jednym:var combined = lists.Aggregate((acc, list) => { return acc.Concat(list); });
źródło
lists
tutaj jako pierwszy? To pierwszy problem OP. Gdyby miałcollection<collection>
od czego zacząć, toSelectMany
jest po prostu prostsze.Użyj w ten
Enumerable.Concat
sposób:var combined = foo.Concat(bar).Concat(baz)....;
źródło
Zastosowanie
Enumerable.Concact
:var query = foo.Concat(bar);
źródło
Zawsze możesz użyć Aggregate w połączeniu z 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();
źródło
SelectMany
jest po prostu prostszy.Jedyny sposób, w jaki widzę, to użycie
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);
Ale powinieneś zdecydować, co jest lepsze:
var result = Combine(foo, bar, tor);
lub
var result = foo.Concat(bar).Concat(tor);
Jednym z powodów
Concat()
, dla których będzie to lepszy wybór, jest to, że będzie to bardziej oczywiste dla innego programisty. Bardziej czytelne i proste.źródło
Możesz użyć Union w następujący sposób:
var combined=foo.Union(bar).Union(baz)...
Spowoduje to jednak usunięcie identycznych elementów, więc jeśli je masz, możesz
Concat
zamiast tego użyć .źródło
Enumerable.Union
jest sumą zbioru, która nie daje pożądanego rezultatu (daje duplikaty tylko raz).int
w moim przykładzie mogło trochę odstraszyć ludzi; aleUnion
nie zadziała w tym przypadku.Kilka technik wykorzystujących inicjatory kolekcji -
zakładając te listy:
var list1 = new List<int> { 1, 2, 3 }; var list2 = new List<int> { 4, 5, 6 };
SelectMany
z inicjatorem tablicy (nie jest to dla mnie zbyt eleganckie, ale nie polega na żadnej funkcji pomocniczej):var combined = new []{ list1, list2 }.SelectMany(x => x);
Zdefiniuj rozszerzenie listy dla Dodaj, które pozwala
IEnumerable<T>
wList<T>
inicjatorze :public static class CollectionExtensions { public static void Add<T>(this ICollection<T> collection, IEnumerable<T> items) { foreach (var item in items) collection.Add(item); } }
Następnie możesz utworzyć nową listę zawierającą elementy innych, takie jak ta (umożliwia to również mieszanie pojedynczych elementów).
var combined = new List<int> { list1, list2, 7, 8 };
źródło
Biorąc pod uwagę, że zaczynasz od kilku oddzielnych kolekcji, myślę, że twoje rozwiązanie jest raczej eleganckie. Będziesz musiał coś zrobić, żeby je połączyć.
Byłoby wygodniej składniowo utworzyć metodę rozszerzającą z metody Combine, która udostępniłaby ją w dowolnym miejscu.
źródło
Wszystko, czego potrzebujesz, to
IEnumerable<IEnumerable<T>> lists
:var combined = lists.Aggregate((l1, l2) => l1.Concat(l2));
Spowoduje to połączenie wszystkich elementów w
lists
jedenIEnumerable<T>
(z duplikatami). UżyjUnion
zamiast,Concat
aby usunąć duplikaty, jak wskazano w innych odpowiedziach.źródło