Powiedzmy, że mam tablicę JavaScript wyglądającą następująco:
["Element 1","Element 2","Element 3",...]; // with close to a hundred elements.
Jakie podejście byłoby odpowiednie, aby podzielić (podzielić) tablicę na wiele mniejszych tablic zawierających, powiedzmy, maksymalnie 10 elementów?
javascript
arrays
split
Przemysłowy
źródło
źródło
const chunk = (arr, n) => arr.length ? [arr.slice(0, n), ...chunk(arr.slice(n), n)] : []
co jest miłe i krótkie, ale wydaje się, że zajmuje około 256 × tak długo, jak odpowiedź @ AymKdn dla 1000 elementów, i 1058 × tak długo dla 10 000 elementów!Odpowiedzi:
Metoda array.slice może wyodrębnić plasterek z początku, środka lub końca tablicy dla dowolnych potrzeb, bez zmiany oryginalnej tablicy.
źródło
chunk
bycia0
. (nieskończona pętla)const array_chunks = (array, chunk_size) => Array(Math.ceil(array.length / chunk_size)).fill().map((_, index) => index * chunk_size).map(begin => array.slice(begin, begin + chunk_size));
.Zmodyfikowano na podstawie odpowiedzi dbaseman: https://stackoverflow.com/a/10456344/711085
drobne uzupełnienie :
Powinienem zauważyć, że powyższe jest nie tak eleganckim (moim zdaniem) obejściem do użycia
Array.map
. Zasadniczo wykonuje następujące czynności, gdzie ~ jest konkatenacją:Ma taki sam asymptotyczny czas działania, jak w poniższej metodzie, ale być może gorszy stały czynnik ze względu na tworzenie pustych list. Można to przepisać w następujący sposób (głównie taki sam jak metoda Blazemongera, dlatego pierwotnie nie przesłałem tej odpowiedzi):
Bardziej wydajna metoda:
Mój ulubiony sposób w dzisiejszych czasach to powyższe lub jedno z poniższych:
Próbny:
Lub jeśli nie chcesz funkcji Array.range, w rzeczywistości jest to tylko jedna linijka (wyłączając puch):
lub
źródło
chunk
funkcją w tablicy nie naprawdę przewyższa dodatkowej złożoności, którą dodajesz, i subtelnych błędów, które mogą powodować bałagan z wbudowanymi prototypami.array.map(function(i)
niearray.map(function(elem,i)
chociażOto wersja ES6 wykorzystująca redukcję
I jesteś gotowy do tworzenia kolejnych map / redukcji transformacji. Twoja tablica wejściowa pozostaje nienaruszona
Jeśli wolisz krótszą, ale mniej czytelną wersję, możesz dodać trochę
concat
do miksu, aby uzyskać ten sam efekt końcowy:źródło
5/2 = 2.5
iMath.floor(2.5) = 2
tak przedmiot o indeksie 5 zostanie umieszczony w segmencie 2Staraj się unikać zmarnowania natywnych prototypów, w tym Array.prototype, jeśli nie wiesz, kto będzie konsumował Twój kod (osoby trzecie, współpracownicy, ty w późniejszym terminie itp.).
Istnieją sposoby bezpiecznego rozszerzania prototypów (ale nie we wszystkich przeglądarkach) i istnieją sposoby bezpiecznego konsumowania obiektów utworzonych z rozszerzonych prototypów, ale lepszą zasadą jest przestrzeganie zasady najmniejszej niespodzianki i całkowite unikanie tych praktyk.
Jeśli masz trochę czasu, obejrzyj wykład JSConf 2011 Andrew Duponta „Wszystko jest dozwolone: rozszerzanie wbudowanych” , aby uzyskać dobrą dyskusję na ten temat.
Ale wracając do pytania, chociaż powyższe rozwiązania będą działać, są one zbyt złożone i wymagają niepotrzebnego narzutu obliczeniowego. Oto moje rozwiązanie:
źródło
Testowałem różne odpowiedzi na stronie jsperf.com. Wynik jest dostępny tam: https://web.archive.org/web/20150909134228/https://jsperf.com/chunk-mtds
A najszybszą funkcją (i działa z IE8) jest ta:
źródło
Wolałbym użyć metody łączenia :
źródło
var clone = [...array]
następnie sprawdź długość i połącz w tej sklonowanej tablicy.array = array.slice()
stworzyć płytką kopię.Jednowarstwowy w ECMA 6
źródło
list
tablicę.map((_,i) => list.slice(i*chuckSize,i*chuckSize+chuckSize))
Stare pytanie: nowa odpowiedź! Właściwie pracowałem z odpowiedzią na to pytanie i mój przyjaciel poprawił to! Oto on:
źródło
.length
jest znacznie większa niż wielkość porcjin
). Gdyby to był leniwy język (w przeciwieństwie do javascript), algorytm ten nie cierpiałby z powodu czasu O (N ^ 2). To powiedziawszy, rekursywna implementacja jest elegancka. Prawdopodobnie możesz go zmodyfikować, aby poprawić wydajność, najpierw definiując funkcję pomocnika, która się powtarzaarray,position
, a następnie wysyłając:Array.prototype.chunk
zwraca [twoją funkcję pomocnika] (...)var chunk = (arr, n) => { if ( !arr.length ) return []; return [ arr.slice( 0, n ) ].concat( chunk(arr.slice(n), n) ) }
W dzisiejszych czasach możesz użyć funkcji fragmentu lodash, aby podzielić tablicę na mniejsze tablice https://lodash.com/docs#chunk Nie musisz już bawić się pętlami!
źródło
Było wiele odpowiedzi, ale używam tego:
Najpierw sprawdź resztę przy dzieleniu indeksu przez wielkość porcji.
Jeśli pozostała resztka, po prostu zwróć zestaw akumulatorów.
Jeśli nie ma reszty, indeks dzieli się przez wielkość porcji, więc weź wycinek z oryginalnej tablicy (zaczynając od bieżącego indeksu) i dodaj go do tablicy akumulatorów.
Tak więc zwrócona tablica akumulatorów dla każdej iteracji redukcji wygląda mniej więcej tak:
źródło
Korzystanie z generatorów
źródło
Ok, zacznijmy od dość ciasnego:
Który jest używany w ten sposób:
Następnie mamy tę funkcję ścisłego reduktora:
Który jest używany w ten sposób:
Ponieważ kotek umiera, gdy łączymy się
this
z liczbą, możemy zamiast tego wykonać ręczne curry:Który jest używany w ten sposób:
Następnie wciąż dość ścisła funkcja, która robi to wszystko za jednym razem:
źródło
(p[i/n|0] || (p[i/n|0] = []))
, abyś nie przypisał wartości, jeśli nie jest to konieczne ...Chciałem stworzyć proste, niemutujące rozwiązanie w czystym ES6. Specyfika javascript wymaga wypełnienia pustej tablicy przed mapowaniem :-(
Ta wersja z rekurencją wydaje się prostsza i bardziej przekonująca:
Śmiesznie słabe funkcje tablicowe ES6 stanowią dobre łamigłówki :-)
źródło
0
zfill
, co sprawia, żefill
wygląd jest nieco bardziej sensowny, imho.Myślę, że to miłe rekurencyjne rozwiązanie ze składnią ES6:
źródło
Utworzono pakiet npm dla tego https://www.npmjs.com/package/array.chunk
Podczas korzystania z TypedArray
źródło
slice
metodyTypedArray
, możemysubarray
zamiast tego użyć developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Jeśli używasz EcmaScript w wersji> = 5.1, możesz zaimplementować funkcjonalną wersję za
chunk()
pomocą array.reduce () o złożoności O (N):Wyjaśnienie każdego z
// nbr
powyższych:chunkSize
elementyCurry oparte na
chunkSize
:Możesz dodać
chunk()
funkcję doArray
obiektu globalnego :źródło
źródło
źródło
Poniższe podejście ES2015 działa bez konieczności definiowania funkcji i bezpośrednio na anonimowych tablicach (przykład z wielkością porcji 2):
Jeśli chcesz zdefiniować dla tego funkcję, możesz to zrobić w następujący sposób (poprawa komentarza K. do odpowiedzi Blazemongera ):
źródło
I to byłby mój wkład w ten temat. Myślę, że
.reduce()
to najlepszy sposób.Ale powyższa implementacja nie jest zbyt wydajna, ponieważ
.reduce()
przebiega przez wszystkiearr
funkcje. Bardziej wydajnym podejściem (bardzo zbliżonym do najszybszego imperatywnego rozwiązania) byłoby iterowanie po zredukowanej (do porcji) tablicy, ponieważ możemy wcześniej obliczyć jej rozmiarMath.ceil(arr/n);
. Gdy mamy już pustą tablicę wyników, podobnie jakArray(Math.ceil(arr.length/n)).fill();
reszta, należy zmapować na nią jej plasterkiarr
.źródło
Użyj fragmentu z lodash
źródło
Aby uzyskać funkcjonalne rozwiązanie, używając Ramdy :
Gdzie
popularProducts
jest twoja tablica wejściowa,5
to wielkość porcjiźródło
ES6 podejście jedna linia na podstawie
Array.prototype
reduce
ipush
metod:źródło
Wersja generatora ES6
źródło
const
zamiast tego.To najbardziej wydajne i proste rozwiązanie, o jakim mogłem myśleć:
źródło
ES6 rozprzestrzenia funkcjonalne #ohmy #ftw
źródło
Oto rekurencyjne rozwiązanie, które polega na optymalizacji ogonów.
źródło
Jeszcze jedno rozwiązanie wykorzystujące
arr.reduce()
:To rozwiązanie jest bardzo podobne do rozwiązania Steve'a Holgado . Ponieważ jednak to rozwiązanie nie wykorzystuje rozkładania tablic i nie tworzy nowych tablic w funkcji reduktora, jest szybsze (patrz test jsPerf ) i subiektywnie bardziej czytelne (prostsza składnia) niż inne rozwiązanie.
Przy każdej n-tej iteracji (gdzie n =
size
; zaczynając od pierwszej iteracji) do tablicy akumulatorów (acc
) dołączany jest fragment tablicy (arr.slice(i, i + size)
), a następnie zwracany. W innych iteracjach tablica akumulatorów jest zwracana w niezmienionej postaci.Jeśli
size
wynosi zero, metoda zwraca pustą tablicę. Jeślisize
jest ujemny, metoda zwraca zepsute wyniki. Tak więc, jeśli zajdzie taka potrzeba, możesz zrobić coś z wartościami ujemnymi lub dodatnimisize
.Jeśli w twoim przypadku ważna jest prędkość, prosta
for
pętla byłaby szybsza niż używaniearr.reduce()
(patrz test jsPerf ), a niektórzy mogą uważać ten styl za bardziej czytelny:źródło
EDYCJA: @ mblase75 dodał bardziej zwięzły kod do wcześniejszej odpowiedzi, kiedy pisałem mój, więc zalecam skorzystanie z jego rozwiązania.
Możesz użyć takiego kodu:
Zmień wartość,
arraySize
aby zmienić maksymalną długość mniejszych tablic.źródło
Oto niemutujące rozwiązanie wykorzystujące tylko rekurencję i slice ().
Następnie po prostu użyj go tak, jak
splitToChunks([1, 2, 3, 4, 5], 3)
chcesz[[1, 2, 3], [4, 5]]
.Oto skrzypce do wypróbowania: https://jsfiddle.net/6wtrbx6k/2/
źródło