Czy istnieje dobry sposób na podzielenie kolekcji na n
części za pomocą LINQ? Oczywiście niekoniecznie równomiernie.
To znaczy, chcę podzielić kolekcję na podkolekcje, z których każda zawiera podzbiór elementów, przy czym ostatnia kolekcja może być poszarpana.
c#
.net
linq
data-structures
Simon_Weaver
źródło
źródło
Odpowiedzi:
Czysty linq i najprostsze rozwiązanie jest pokazane poniżej.
źródło
.AsEnumerable()
nie jest konieczne, IGrouping <T> jest już IEnumerable <T>.EDYCJA: OK, wygląda na to, że źle odczytałem pytanie. Czytam to jako „kawałki o długości n”, a nie „n sztuk”. No! Rozważam usunięcie odpowiedzi ...
(Oryginalna odpowiedź)
Nie wierzę, że istnieje wbudowany sposób partycjonowania, chociaż zamierzam napisać go w moim zestawie dodatków do LINQ to Objects. Marc Gravell ma tutaj implementację, chociaż prawdopodobnie zmodyfikowałbym ją, aby zwracała widok tylko do odczytu:
źródło
yield return
. Wymaga, aby jedna partia znajdowała się w pamięci naraz, ale to wszystko.źródło
var dept = {1,2,3,4,5}
. Po podzieleniu wynik jest jakdept1 = {1,3,5}
idept2 = { 2,4 }
gdzieparts = 2
. Ale wynik jest mi potrzebnedept1 = {1,2,3}
idept2 = {4,5}
int columnLength = (int)Math.Ceiling((decimal)(list.Count()) / parts);
następnie wykonałem podział.GroupBy(x => x.index / columnLength)
. Jedynym minusem jest to, że Count () wylicza listę.Ok, wrzucę kapelusz na ring. Zalety mojego algorytmu:
Kod:
Jak wskazano w komentarzach poniżej, podejście to w rzeczywistości nie odnosi się do pierwotnego pytania, które dotyczyło stałej liczby odcinków o mniej więcej równej długości. To powiedziawszy, nadal możesz zastosować moje podejście do rozwiązania pierwotnego pytania, nazywając je w ten sposób:
Gdy jest używany w ten sposób, podejście nie jest już O (1), ponieważ operacja Count () to O (N).
źródło
To jest to samo, co przyjęta odpowiedź, ale o wiele prostsza reprezentacja:
Powyższa metoda dzieli an
IEnumerable<T>
na liczbę N fragmentów o równych lub zbliżonych rozmiarach.Powyższa metoda dzieli plik
IEnumerable<T>
na kawałki o pożądanym ustalonym rozmiarze, przy czym całkowita liczba fragmentów jest nieistotna - nie o to chodzi w pytaniu.Problem z
Split
metodą, poza tym, że jest wolniejsza, polega na tym, że szyfruje ona dane wyjściowe w tym sensie, że grupowanie zostanie wykonane na podstawie i-tej wielokrotności N dla każdej pozycji, lub innymi słowy, nie dostaniesz fragmentów w pierwotnej kolejności.Prawie każda odpowiedź tutaj albo nie zachowuje porządku, albo dotyczy partycjonowania, a nie dzielenia, albo jest po prostu błędna. Spróbuj tego, co jest szybsze, zachowuje porządek, ale jest trochę bardziej szczegółowe:
Równoważna metoda dla
Partition
operacji tutajźródło
Dość często korzystałem z funkcji Partition, którą zamieściłem wcześniej. Jedyną złą rzeczą było to, że nie było w pełni przesyłania strumieniowego. Nie stanowi to problemu, jeśli pracujesz z kilkoma elementami w sekwencji. Potrzebowałem nowego rozwiązania, kiedy zacząłem pracować z ponad 100 000 elementami w mojej sekwencji.
Poniższe rozwiązanie jest dużo bardziej złożone (i zawiera więcej kodu!), Ale jest bardzo wydajne.
Cieszyć się!
źródło
Ciekawy wątek. Aby uzyskać wersję strumieniową Split / Partition, można użyć modułów wyliczających i uzyskać sekwencje z modułu wyliczającego przy użyciu metod rozszerzających. Konwersja kodu imperatywnego na kod funkcjonalny przy użyciu wydajności jest rzeczywiście bardzo potężną techniką.
Najpierw rozszerzenie modułu wyliczającego, które zamienia liczbę elementów w leniwą sekwencję:
A potem wyliczalne rozszerzenie, które dzieli sekwencję:
Efektem końcowym jest wysoce wydajna, strumieniowa i leniwa implementacja, która opiera się na bardzo prostym kodzie.
Cieszyć się!
źródło
Używam tego:
źródło
Jest to wydajne pod względem pamięci i opóźnia wykonanie tak bardzo, jak to możliwe (na partię) i działa w czasie liniowym O (n)
źródło
Istnieje wiele świetnych odpowiedzi na to pytanie (i jego kuzynów). Sam tego potrzebowałem i stworzyłem rozwiązanie zaprojektowane tak, aby było wydajne i odporne na błędy w scenariuszu, w którym zbiór źródłowy może być traktowany jako lista. Nie używa żadnej leniwej iteracji, więc może nie być odpowiedni dla kolekcji o nieznanym rozmiarze, które mogą obciążać pamięć.
Widziałem kilka odpowiedzi w tej rodzinie pytań, które używają GetRange i Math.Min. Uważam jednak, że ogólnie jest to bardziej kompletne rozwiązanie pod względem sprawdzania błędów i wydajności.
źródło
źródło
Świetne odpowiedzi, dla mojego scenariusza przetestowałem zaakceptowaną odpowiedź i wygląda na to, że nie utrzymuje porządku. Jest też świetna odpowiedź Nawfala, która utrzymuje porządek. Ale w moim scenariuszu chciałem podzielić resztę w znormalizowany sposób, wszystkie odpowiedzi, które widziałem, rozprzestrzeniły się na resztę lub na początku lub na końcu.
Moja odpowiedź obejmuje również dalsze rozprzestrzenianie się w bardziej znormalizowany sposób.
źródło
Jeśli kolejność w tych częściach nie jest zbyt ważna, możesz spróbować tego:
Jednak z jakiegoś powodu nie można ich rzutować na IEnumerable <IEnumerable <int>> ...
źródło
To jest mój kod, ładny i krótki.
źródło
To mój sposób, wyliczając przedmioty i dzieląc wiersz po kolumnach
źródło
Szukałem podziału takiego jak ten ze stringiem, więc cała Lista jest podzielona według jakiejś zasady, nie tylko pierwsza część, to moje rozwiązanie
źródło
Oto mała zmiana liczby przedmiotów zamiast liczby części:
źródło
źródło
Właśnie natknąłem się na ten wątek, a większość rozwiązań tutaj polega na dodawaniu elementów do kolekcji, skutecznie materializując każdą stronę przed jej zwróceniem. Jest to złe z dwóch powodów - po pierwsze, jeśli twoje strony są duże, istnieje narzut pamięci na wypełnienie strony, po drugie istnieją iteratory, które unieważniają poprzednie rekordy po przejściu do następnego (na przykład, jeśli zawiniesz DataReader w metodzie wyliczającej) .
To rozwiązanie wykorzystuje dwie zagnieżdżone metody modułu wyliczającego, aby uniknąć konieczności buforowania elementów w tymczasowych kolekcjach. Ponieważ iteratory zewnętrzne i wewnętrzne przechodzą przez ten sam wyliczalny, koniecznie współużytkują ten sam moduł wyliczający, dlatego ważne jest, aby nie przesuwać zewnętrznego, dopóki nie zakończysz przetwarzania bieżącej strony. To powiedziawszy, jeśli zdecydujesz się nie iterować przez całą bieżącą stronę, po przejściu do następnej strony to rozwiązanie automatycznie iteruje w przód do granicy strony.
źródło