Czy jest jakiś sposób na podzielenie a List<SomeObject>
na kilka osobnych list SomeObject
, używając indeksu pozycji jako ogranicznika każdego podziału?
Pozwól, że zilustruję:
Mam List<SomeObject>
i ja potrzebujemy List<List<SomeObject>>
lub List<SomeObject>[]
, tak że każdy z tych wynikających list będzie zawierał grupę 3 sztuk na pierwotnej liście (kolejno).
na przykład.:
Oryginalna lista:
[a, g, e, w, p, s, q, f, x, y, i, m, c]
Listy wynikowe:
[a, g, e], [w, p, s], [q, f, x], [y, i, m], [c]
Potrzebowałbym również wynikowego rozmiaru list, aby był parametrem tej funkcji.
źródło
[a,g,e]
przed wyliczeniem oryginalnej listy.GroupBy(x=>f(x)).First()
nigdy nie da grupy. OP zapytał o listy, ale jeśli piszemy do pracy z IEnumerable, wykonując tylko jedną iterację, czerpiemy przewagę wydajności.To pytanie jest trochę stare, ale właśnie to napisałem i myślę, że jest nieco bardziej eleganckie niż inne proponowane rozwiązania:
źródło
if (chunksize <= 0) throw new ArgumentException("Chunk size must be greater than zero.", "chunksize");
O(n²)
. Możesz iterować po liście i miećO(n)
czas.source
zaIEnumerable
każdym razem zastępowane jest opakowaniem . Więc pobieranie elementówsource
przechodzi przez warstwySkip
sOgólnie rzecz biorąc, podejście sugerowane przez CaseyB działa dobrze, w rzeczywistości, jeśli przechodzisz
List<T>
, trudno go winić, być może zmieniłbym to na:Co pozwoli uniknąć masowych łańcuchów połączeń. Niemniej jednak podejście to ma ogólną wadę. Zmaterializuje dwa wyliczenia na porcję, aby podkreślić problem, spróbuj uruchomić:
Aby przezwyciężyć ten problem, możemy wypróbować podejście Camerona , które przechodzi powyższy test w żywych kolorach, ponieważ chodzi o wyliczenie tylko raz.
Problem polega na tym, że ma inną wadę, materializuje każdy przedmiot w każdym kawałku, problem z tym podejściem polega na tym, że zabrakło ci pamięci.
Aby to zilustrować, spróbuj uruchomić:
Wreszcie każda implementacja powinna być w stanie obsłużyć iterację porcji poza kolejnością, na przykład:
Wiele wysoce optymalnych rozwiązań, takich jak moja pierwsza wersja tej odpowiedzi, zawiodło. Ten sam problem można znaleźć w zoptymalizowanej odpowiedzi casperOne .
Aby rozwiązać wszystkie te problemy, możesz użyć:
Istnieje również runda optymalizacji, które można wprowadzić w celu iteracji porcji poza kolejnością, co jest tutaj poza zakresem.
Jaką metodę wybrać? Zależy to całkowicie od problemu, który próbujesz rozwiązać. Jeśli nie przejmujesz się pierwszą wadą, prosta odpowiedź jest niezwykle atrakcyjna.
Uwaga: jak w przypadku większości metod, nie jest to bezpieczne w przypadku wielowątkowości, rzeczy mogą stać się dziwne, jeśli chcesz, aby wątek był bezpieczny, musisz wprowadzić poprawki
EnumeratorWrapper
.źródło
Państwo mogli korzystać z wielu pytań, które używają
Take
iSkip
, ale to byłoby zbyt wielu iteracji na pierwotnej liście, wierzę.Uważam raczej, że powinieneś stworzyć własny iterator, taki jak:
Następnie możesz to wywołać, a funkcja LINQ jest włączona, dzięki czemu możesz wykonywać inne operacje na wynikowych sekwencjach.
W świetle odpowiedzi Sama czułem, że istnieje łatwiejszy sposób na zrobienie tego bez:
To powiedziawszy, oto kolejna przepustka, którą skodyfikowałem metodą rozszerzenia do
IEnumerable<T>
wywołaniaChunk
:Nic dziwnego, tylko podstawowe sprawdzanie błędów.
Przechodząc do
ChunkInternal
:Zasadniczo dostaje
IEnumerator<T>
i ręcznie iteruje każdy element. Sprawdza, czy są jakieś elementy do wyliczenia. Po wyliczeniu każdego fragmentu, jeśli nie ma już żadnych elementów, wybucha.Po wykryciu elementów w sekwencji przekazuje odpowiedzialność za
IEnumerable<T>
implementację wewnętrzną doChunkSequence
:Ponieważ
MoveNext
został już przywołany doIEnumerator<T>
przekazanegoChunkSequence
, zwraca przedmiot o,Current
a następnie zwiększa liczbę, upewniając się, że nigdy nie zwróci więcej niżchunkSize
elementów i przechodzi do następnego elementu w sekwencji po każdej iteracji (ale jest zwarty, jeśli liczba uzyskane elementy przekraczają rozmiar porcji).Jeśli nie pozostały żadne elementy,
InternalChunk
metoda wykona kolejne przejście w zewnętrznej pętli, ale gdyMoveNext
zostanie wywołana po raz drugi, nadal zwróci false, zgodnie z dokumentacją (moje wyróżnienie):W tym momencie pętla pęknie, a sekwencja sekwencji zakończy się.
To jest prosty test:
Wynik:
Ważna uwaga, to nie zadziała, jeśli nie wyczerpiesz całej sekwencji potomnej lub nie przerwiesz jej w żadnym punkcie sekwencji rodzicielskiej. Jest to ważne zastrzeżenie, ale jeśli twój przypadek użycia jest taki, że zużyjesz każdy element sekwencji sekwencji, to zadziała dla ciebie.
Dodatkowo, będzie to robić dziwne rzeczy, jeśli będziesz grał z rozkazem, tak jak zrobił to Sam w pewnym momencie .
źródło
List<T>
, oczywiście będziesz mieć problemy z pamięcią z powodu buforowania. Z perspektywy czasu powinienem był to zauważyć w odpowiedzi, ale wydawało się, że w tamtym czasie koncentrowano się na zbyt wielu iteracjach. To powiedziawszy, twoje rozwiązanie jest rzeczywiście bardziej włochate. Nie testowałem tego, ale teraz zastanawiam się, czy istnieje mniej owłosione rozwiązanie.Ok, oto moje zdanie na ten temat:
Przykładowe użycie
Objaśnienia
Kod działa poprzez zagnieżdżenie dwóch
yield
iteratorów opartych na.Zewnętrzny iterator musi śledzić, ile elementów zostało skutecznie pochłoniętych przez wewnętrzny (fragment) iterator. Odbywa się to poprzez zamknięcie za
remaining
pomocąinnerMoveNext()
. Niewykorzystane elementy fragmentu są odrzucane, zanim następny fragment zostanie podany przez zewnętrzny iterator. Jest to konieczne, ponieważ w przeciwnym razie otrzymujesz niespójne wyniki, gdy wewnętrzne wyliczenia nie zostaną (całkowicie) wykorzystane (np.c3.Count()
Zwrócą 6).źródło
całkowicie leniwy, bez liczenia i kopiowania:
źródło
Myślę, że następująca sugestia byłaby najszybsza. Poświęcam lenistwo źródła Enumerable ze względu na możliwość korzystania z Array.Copy i znajomość z góry długości każdej z moich podlist.
źródło
Możemy ulepszyć rozwiązanie @ JaredPar, aby przeprowadzić prawdziwie leniwą ocenę. Używamy
GroupAdjacentBy
metody, która daje grupy kolejnych elementów z tym samym kluczem:Ponieważ grupy są uzyskiwane jeden po drugim, to rozwiązanie działa skutecznie z długimi lub nieskończonymi sekwencjami.
źródło
Kilka lat temu napisałem metodę rozszerzenia Clumpa. Działa świetnie i jest najszybszą implementacją tutaj. : P
źródło
System.Interactive zapewnia
Buffer()
w tym celu. Niektóre szybkie testy pokazują, że wydajność jest podobna do rozwiązania Sama.źródło
Buffer()
zwraca,IEnumerable<IList<T>>
więc tak, prawdopodobnie miałbyś problem - nie działa tak jak twój.Oto procedura podziału listy, którą napisałem kilka miesięcy temu:
źródło
Uważam, że ten krótki fragment całkiem dobrze sobie radzi.
źródło
A co z tym?
O ile mi wiadomo, GetRange () jest liniowy pod względem liczby pobranych elementów. To powinno działać dobrze.
źródło
To stare pytanie, ale z tym skończyłem; wylicza wyliczalny tylko raz, ale tworzy listy dla każdej partycji. Nie jest narażony na nieoczekiwane zachowanie, gdy
ToArray()
jest wywoływany, podobnie jak niektóre implementacje:źródło
public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> source, int chunkSize)
Okazało się, że rozwiązanie Davida B. działało najlepiej. Ale dostosowaliśmy go do bardziej ogólnego rozwiązania:
źródło
Poniższe rozwiązanie jest najbardziej kompaktowe, jakie mogłem wymyślić, czyli O (n).
źródło
Stary kod, ale tego właśnie używałem:
źródło
Jeśli lista jest typu system.collections.generic, możesz skorzystać z dostępnej metody „CopyTo”, aby skopiować elementy tablicy na inne tablice podrzędne. Określ element początkowy i liczbę elementów do skopiowania.
Możesz także zrobić 3 klony z oryginalnej listy i użyć „RemoveRange” na każdej liście, aby zmniejszyć listę do pożądanego rozmiaru.
Lub po prostu stwórz metodę pomocniczą, aby zrobić to za Ciebie.
źródło
To stare rozwiązanie, ale miałem inne podejście. Używam,
Skip
aby przejść do pożądanego przesunięcia iTake
wyodrębnić pożądaną liczbę elementów:źródło
Dla wszystkich zainteresowanych pakietem / utrzymywanym rozwiązaniem biblioteka MoreLINQ zapewnia
Batch
metodę rozszerzenia pasującą do żądanego zachowania:Batch
Realizacja jest podobna do odpowiedzi Cameron MacFarland za , z dodatkiem przeciążenia dla przekształcenia klocek / partię przed powrotem i wykonuje całkiem dobrze.źródło
Korzystanie z partycjonowania modułowego:
źródło
Właśnie włożyłem moje dwa centy. Jeśli chcesz „wstawić” listę (wizualizować od lewej do prawej), możesz wykonać następujące czynności:
źródło
Innym sposobem jest użycie operatora bufora Rx
źródło
źródło
Przyjąłem pierwotną odpowiedź i postanowiłem, że jest to pojemnik MKOl, aby ustalić, gdzie podzielić. ( Bo kto tak naprawdę chce podzielić tylko na 3 elementy, czytając ten post podczas szukania odpowiedzi? )
Ta metoda pozwala na podział na dowolny typ przedmiotu w razie potrzeby.
Tak więc dla OP kod byłby
źródło
Tak spektakularne, jak podejście Sama Saffrona .
}
źródło
Może współpracować z nieskończonymi generatorami:
Kod demonstracyjny: https://ideone.com/GKmL7M
Ale tak naprawdę wolałbym pisać odpowiednią metodę bez linq.
źródło
Spójrz na to! Mam listę elementów z licznikiem sekwencji i datą. Za każdym razem, gdy sekwencja uruchamia się ponownie, chcę utworzyć nową listę.
Dawny. lista wiadomości.
Chcę podzielić listę na osobne listy, gdy licznik uruchomi się ponownie. Oto kod:
źródło
Aby wstawić moje dwa centy ...
Korzystając z typu listy dla fragmentu źródła, znalazłem inne bardzo kompaktowe rozwiązanie:
źródło