Podziel tablicę na części


516

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?




24
Dla użytkowników lodash szukasz _.chunk .
Ulysse BN

2
Właśnie próbowałem, 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!
Toph

jeśli potrzebujesz również minimalnego rozmiaru ostatniego fragmentu, oto opcje: stackoverflow.com/questions/57908133/…
Ahmet Cetin

Odpowiedzi:


640

Metoda array.slice może wyodrębnić plasterek z początku, środka lub końca tablicy dla dowolnych potrzeb, bez zmiany oryginalnej tablicy.

var i,j,temparray,chunk = 10;
for (i=0,j=array.length; i<j; i+=chunk) {
    temparray = array.slice(i,i+chunk);
    // do whatever
}

19
Pamiętaj, że jeśli jest to funkcja util dochodzić od chunkbycia 0. (nieskończona pętla)
Steven Lu

19
Nie, ostatni fragment powinien być po prostu mniejszy od pozostałych.
Blazemonger,

7
@Blazemonger, rzeczywiście! Następnym razem spróbuję tego sam, zanim przejdę do wniosków. Założyłem (niepoprawnie), że przekazanie danych wejściowych do tablicy.slice, która przekroczyła granice tablicy, byłoby problemem, ale działa idealnie!
rysqui

55
Dla jednej wkładki (łańcuch-lovers) : 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));.
Константин Ван

8
Dlaczego potrzebujesz j? Najpierw pomyślałem, że jest to optymalizacja, ale tak naprawdę jest wolniejsza niż dla (i = 0; i <array.length; i ++) {}
Alex

149

Zmodyfikowano na podstawie odpowiedzi dbaseman: https://stackoverflow.com/a/10456344/711085

Object.defineProperty(Array.prototype, 'chunk_inefficient', {
  value: function(chunkSize) {
    var array = this;
    return [].concat.apply([],
      array.map(function(elem, i) {
        return i % chunkSize ? [] : [array.slice(i, i + chunkSize)];
      })
    );
  }
});

console.log(
  [1, 2, 3, 4, 5, 6, 7].chunk_inefficient(3)
)
// [[1, 2, 3], [4, 5, 6], [7]]


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ą:

[[1,2,3]]~[]~[]~[] ~ [[4,5,6]]~[]~[]~[] ~ [[7]]

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:

// refresh page if experimenting and you already defined Array.prototype.chunk

Object.defineProperty(Array.prototype, 'chunk', {
  value: function(chunkSize) {
    var R = [];
    for (var i = 0; i < this.length; i += chunkSize)
      R.push(this.slice(i, i + chunkSize));
    return R;
  }
});

console.log(
  [1, 2, 3, 4, 5, 6, 7].chunk(3)
)


Mój ulubiony sposób w dzisiejszych czasach to powyższe lub jedno z poniższych:

Array.range = function(n) {
  // Array.range(5) --> [0,1,2,3,4]
  return Array.apply(null,Array(n)).map((x,i) => i)
};

Object.defineProperty(Array.prototype, 'chunk', {
  value: function(n) {

    // ACTUAL CODE FOR CHUNKING ARRAY:
    return Array.range(Math.ceil(this.length/n)).map((x,i) => this.slice(i*n,i*n+n));

  }
});

Próbny:

> JSON.stringify( Array.range(10).chunk(3) );
[[1,2,3],[4,5,6],[7,8,9],[10]]

Lub jeśli nie chcesz funkcji Array.range, w rzeczywistości jest to tylko jedna linijka (wyłączając puch):

var ceil = Math.ceil;

Object.defineProperty(Array.prototype, 'chunk', {value: function(n) {
    return Array(ceil(this.length/n)).fill().map((_,i) => this.slice(i*n,i*n+n));
}});

lub

Object.defineProperty(Array.prototype, 'chunk', {value: function(n) {
    return Array.from(Array(ceil(this.length/n)), (_,i)=>this.slice(i*n,i*n+n));
}});

41
Eh, unikałbym bałagania w prototypie, ponieważ poczucie chłodu wywołanego chunkfunkcją 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.
Gordon Gustafson,

12
Nie zadziera z nimi, rozszerza je na tablice. Rozumiem, że nigdy nie dotykam prototypu Object.prototype, ponieważ spowoduje to bąbelkowanie do wszystkich obiektów (wszystkiego), ale dla tej funkcji specyficznej dla tablicy nie widzę żadnych problemów.
rlemon

Pewien, że powinna być array.map(function(i)nie array.map(function(elem,i)chociaż
Nigel Anioł

3
Na podstawie tabeli kompatybilności na stronie dewelopera mozilla, Array.map dla IE9 +. Bądź ostrożny.
Maikel D

Uważaj, aby podać liczbę zmiennoprzecinkową - 7,5 typu liczb - prowadzić do nieprzewidywalnych kawałków
Gal Bracha

103

Oto wersja ES6 wykorzystująca redukcję

var perChunk = 2 // items per chunk    

var inputArray = ['a','b','c','d','e']

var result = inputArray.reduce((resultArray, item, index) => { 
  const chunkIndex = Math.floor(index/perChunk)

  if(!resultArray[chunkIndex]) {
    resultArray[chunkIndex] = [] // start a new chunk
  }

  resultArray[chunkIndex].push(item)

  return resultArray
}, [])

console.log(result); // result: [['a','b'], ['c','d'], ['e']]

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ę concatdo miksu, aby uzyskać ten sam efekt końcowy:

inputArray.reduce((all,one,i) => {
   const ch = Math.floor(i/perChunk); 
   all[ch] = [].concat((all[ch]||[]),one); 
   return all
}, [])

To wydaje się najbardziej skondensowanym rozwiązaniem. Co dostaje chunkIndex = Math.floor (index / perChunk)? Czy to średnia?
me-me

5/2 = 2.5i Math.floor(2.5) = 2tak przedmiot o indeksie 5 zostanie umieszczony w segmencie 2
Andrei R

Dzięki Andrei R. Ah, więc przechodzi przez 0 - 2. Jak to się nazywa w kategoriach matematycznych? Chyba chodzi mi o to, że nigdy nie pomyślałbym o podzieleniu indeksu / 2 każdego indeksu, aby uzyskać indeks każdego wycinka. Staram się więc owinąć głowę, ponieważ bardzo mi się podoba, ale nie do końca rozumiem w kategoriach matematycznych. Zwykle robię to, aby uzyskać średnie liczby całkowitej, ale wygląda to inaczej.
me-me

To rozwiązanie jest nieefektywne w porównaniu z innymi rozwiązaniami, ponieważ musisz iterować każdy element.
JP de la Torre,

masz rację @JPdelaTorre, prawdopodobnie nie jest tak skuteczny jak rozwiązania, które kroją tablice, ale tutaj dzielisz włosy. większość wymienionych odpowiedzi byłaby z tego powodu nieefektywna.
Andrei R

102

Staraj 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:

function chunk (arr, len) {

  var chunks = [],
      i = 0,
      n = arr.length;

  while (i < n) {
    chunks.push(arr.slice(i, i += len));
  }

  return chunks;
}

// Optionally, you can do the following to avoid cluttering the global namespace:
Array.chunk = chunk;

25
„unikaj nawijania przy użyciu natywnych prototypów” nowi programiści js powinni uzyskać tymczasowy, jeśli nie stały, tatuaż tej frazy.
Jacob Dalton

2
Używam javascript od lat i prawie nigdy nie kłopoczę się prototypem, przy większości funkcji wywoływania, nigdy nie modyfikując tak, jak robią to niektórzy ludzie.
1984

2
najlepsza sugestia w moich oczach, najprostsza do zrozumienia i realizacji, bardzo dziękuję!
Olga Farber

1
@JacobDalton Myślę, że to wina wszystkich uniwersytetów. Ludzie myślą, że OOP musi być używane wszędzie. Dlatego boją się „tworzenia funkcji”. Chcą mieć pewność, że coś w to włoży. Nawet jeśli w ogóle nie jest to właściwe. Jeśli nie ma notacji kropkowej, nie ma „architektury”.
Gherman

51

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:

function chunk(arr, chunkSize) {
  var R = [];
  for (var i=0,len=arr.length; i<len; i+=chunkSize)
    R.push(arr.slice(i,i+chunkSize));
  return R;
}

1
Dzięki @AymKdn za dokonanie tego testu porównawczego: To było bardzo pomocne! I był przy użyciu podejścia spawów i rozbił moją przeglądarkę V70 Chrome w rozmiarze klocek 884432. ze swoimi sugerowanego podejścia „slice” w miejscu, mój kod nie crash „render” proces przeglądarkę już. :)
Benny Neugebauer,

34

Wolałbym użyć metody łączenia :

var chunks = function(array, size) {
  var results = [];
  while (array.length) {
    results.push(array.splice(0, size));
  }
  return results;
};

16
Należy zachować ostrożność przy tym rozwiązaniu łączenia, ponieważ modyfikuje on oryginalną tablicę, więc należy wyraźnie udokumentować skutki uboczne.
bdrx

3
Następnie użyj plasterka
mplungjan 24.04.15

@mplungjan Rezultatem byłaby ta sama tablica w kółko podczas używania plasterka. Więc to nie jest tak naprawdę zamiennik bez kilku modyfikacji.
nietonfir

Jedyne, co chciałbym dodać do tej odpowiedzi, to klon oryginalnej tablicy. Zrobiłbym to z operatorem rozprzestrzeniania ES6. var clone = [...array]następnie sprawdź długość i połącz w tej sklonowanej tablicy.
MauricioLeal,

1
Lub jeśli nie możesz korzystać z funkcji ES6, możesz po prostu array = array.slice()stworzyć płytką kopię.
3limin4t0r

34

Jednowarstwowy w ECMA 6

const [list,chuckSize] = [[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], 6]

new Array(Math.ceil(list.length / chuckSize)).fill().map(_ => list.splice(0,chuckSize))

9
modyfikuje oryginalną listtablicę
Jacka

6
Łatwa naprawa za pomocą .slice () .. .map((_,i) => list.slice(i*chuckSize,i*chuckSize+chuckSize))
James Robey,

2
Na JSPerf jest to znacznie bardziej wydajne niż wiele innych odpowiedzi.
Micheasz Henning,

30

Stare pytanie: nowa odpowiedź! Właściwie pracowałem z odpowiedzią na to pytanie i mój przyjaciel poprawił to! Oto on:

Array.prototype.chunk = function ( n ) {
    if ( !this.length ) {
        return [];
    }
    return [ this.slice( 0, n ) ].concat( this.slice(n).chunk(n) );
};

[1,2,3,4,5,6,7,8,9,0].chunk(3);
> [[1,2,3],[4,5,6],[7,8,9],[0]]

14
fyi, wydajność tej metody wynosi O (N ^ 2), więc nie należy jej używać w krytycznych pod względem wydajności sekcjach kodu lub z długimi tablicami (szczególnie, gdy tablica .lengthjest znacznie większa niż wielkość porcji n). 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ę powtarza array,position, a następnie wysyłając: Array.prototype.chunkzwraca [twoją funkcję pomocnika] (...)
ninjagecko

dzięki sensai za pobłogosławienie mnie waszym duchem, który musiałem dziś wieczorem intonować
ugryziony

1
lub ...var chunk = (arr, n) => { if ( !arr.length ) return []; return [ arr.slice( 0, n ) ].concat( chunk(arr.slice(n), n) ) }
Ed Williams

28

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!


3
Wydaje mi się, że powinno być zastrzeżenie dla SO pytań javascript: czy próbowałeś lodash? Prawie pierwsza rzecz, którą zawarłem w węźle lub przeglądarce.
Jacob Dalton,

20

Było wiele odpowiedzi, ale używam tego:

const chunk = (arr, size) =>
  arr
    .reduce((acc, _, i) =>
      (i % size)
        ? acc
        : [...acc, arr.slice(i, i + size)]
    , [])

// USAGE
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
chunk(numbers, 3)

// [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

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:

// 0: [[1, 2, 3]]
// 1: [[1, 2, 3]]
// 2: [[1, 2, 3]]
// 3: [[1, 2, 3], [4, 5, 6]]
// 4: [[1, 2, 3], [4, 5, 6]]
// 5: [[1, 2, 3], [4, 5, 6]]
// 6: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// 7: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// 8: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// 9: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

Ładne rozwiązanie i ładne wizualne przedstawienie iteracji. Skończyłem z bardzo podobnym rozwiązaniem, które opublikowałem jako odpowiedź: stackoverflow.com/a/60779547
mts knn

15

Korzystanie z generatorów

function* chunks(arr, n) {
 for(let i = 0; i < arr.length; i += n) {
     yield(arr.slice(i, i+n));
     }
}
let someArray = [0,1,2,3,4,5,6,7,8,9]
console.log([...chunks(someArray, 2)]) // [[0,1],[2,3],[4,5],[6,7],[8,9]]


3
Byłem zaskoczony wszystkimi odpowiedziami na to pytanie, które prawie używały generatorów lub korzystały z nich w bardziej skomplikowany sposób. Niezła zwięzłość i wydajność dzięki temu rozwiązaniu.
Keith

14

Ok, zacznijmy od dość ciasnego:

function chunk(arr, n) {
    return arr.slice(0,(arr.length+n-1)/n|0).
           map(function(c,i) { return arr.slice(n*i,n*i+n); });
}

Który jest używany w ten sposób:

chunk([1,2,3,4,5,6,7], 2);

Następnie mamy tę funkcję ścisłego reduktora:

function chunker(p, c, i) {
    (p[i/this|0] = p[i/this|0] || []).push(c);
    return p;
}

Który jest używany w ten sposób:

[1,2,3,4,5,6,7].reduce(chunker.bind(3),[]);

Ponieważ kotek umiera, gdy łączymy się thisz liczbą, możemy zamiast tego wykonać ręczne curry:

// Fluent alternative API without prototype hacks.
function chunker(n) {
   return function(p, c, i) {
       (p[i/n|0] = p[i/n|0] || []).push(c);
       return p;
   };
}

Który jest używany w ten sposób:

[1,2,3,4,5,6,7].reduce(chunker(3),[]);

Następnie wciąż dość ścisła funkcja, która robi to wszystko za jednym razem:

function chunk(arr, n) {
    return arr.reduce(function(p, cur, i) {
        (p[i/n|0] = p[i/n|0] || []).push(cur);
        return p;
    },[]);
}

chunk([1,2,3,4,5,6,7], 3);

Nie działa w iE8.
Nadeemmnn Mohd

4
HA! uwielbiam komentarz kotka. przepraszam za brak konstruktywnego wkładu :)
29

Zrobiłbym to (p[i/n|0] || (p[i/n|0] = [])), abyś nie przypisał wartości, jeśli nie jest to konieczne ...
yckart

12

Chciałem stworzyć proste, niemutujące rozwiązanie w czystym ES6. Specyfika javascript wymaga wypełnienia pustej tablicy przed mapowaniem :-(

function chunk(a, l) { 
    return new Array(Math.ceil(a.length / l)).fill(0)
        .map((_, n) => a.slice(n*l, n*l + l)); 
}

Ta wersja z rekurencją wydaje się prostsza i bardziej przekonująca:

function chunk(a, l) { 
    if (a.length == 0) return []; 
    else return [a.slice(0, l)].concat(chunk(a.slice(l), l)); 
}

Śmiesznie słabe funkcje tablicowe ES6 stanowią dobre łamigłówki :-)


1
Tak też napisałem mój. Nadal działa, jeśli usuniesz 0z fill, co sprawia, że fillwygląd jest nieco bardziej sensowny, imho.
Coert Grobbelaar

12

Myślę, że to miłe rekurencyjne rozwiązanie ze składnią ES6:

const chunk = function(array, size) {
  if (!array.length) {
    return [];
  }
  const head = array.slice(0, size);
  const tail = array.slice(size);

  return [head, ...chunk(tail, size)];
};

console.log(chunk([1,2,3], 2));


9

Utworzono pakiet npm dla tego https://www.npmjs.com/package/array.chunk

var result = [];

for (var i = 0; i < arr.length; i += size) {
  result.push(arr.slice(i, size + i));
}
return result;

Podczas korzystania z TypedArray

var result = [];

for (var i = 0; i < arr.length; i += size) {
  result.push(arr.subarray(i, size + i));
}
return result;

@ A1rPun Mój zły, nie dodałem tam komentarza. Tak, nie ma slicemetody TypedArray, możemy subarrayzamiast tego użyć developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
zhiyelee

8

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):

function chunk(chunkSize, array) {
    return array.reduce(function(previous, current) {
        var chunk;
        if (previous.length === 0 || 
                previous[previous.length -1].length === chunkSize) {
            chunk = [];   // 1
            previous.push(chunk);   // 2
        }
        else {
            chunk = previous[previous.length -1];   // 3
        }
        chunk.push(current);   // 4
        return previous;   // 5
    }, []);   // 6
}

console.log(chunk(2, ['a', 'b', 'c', 'd', 'e']));
// prints [ [ 'a', 'b' ], [ 'c', 'd' ], [ 'e' ] ]

Wyjaśnienie każdego z // nbrpowyższych:

  1. Utwórz nową porcję, jeśli poprzednia wartość, tj. Poprzednio zwrócona tablica porcji, jest pusta lub jeśli ostatnia poprzednia porcja zawiera chunkSizeelementy
  2. Dodaj nową porcję do tablicy istniejących porcji
  3. W przeciwnym razie bieżąca porcja jest ostatnią porcją w tablicy porcji
  4. Dodaj bieżącą wartość do porcji
  5. Zwraca zmodyfikowaną tablicę porcji
  6. Zainicjuj redukcję, przekazując pustą tablicę

Curry oparte na chunkSize:

var chunk3 = function(array) {
    return chunk(3, array);
};

console.log(chunk3(['a', 'b', 'c', 'd', 'e']));
// prints [ [ 'a', 'b', 'c' ], [ 'd', 'e' ] ]

Możesz dodać chunk()funkcję do Arrayobiektu globalnego :

Object.defineProperty(Array.prototype, 'chunk', {
    value: function(chunkSize) {
        return this.reduce(function(previous, current) {
            var chunk;
            if (previous.length === 0 || 
                    previous[previous.length -1].length === chunkSize) {
                chunk = [];
                previous.push(chunk);
            }
            else {
                chunk = previous[previous.length -1];
            }
            chunk.push(current);
            return previous;
        }, []);
    }
});

console.log(['a', 'b', 'c', 'd', 'e'].chunk(4));
// prints [ [ 'a', 'b', 'c' 'd' ], [ 'e' ] ]


7
in coffeescript:

b = (a.splice(0, len) while a.length)

demo 
a = [1, 2, 3, 4, 5, 6, 7]

b = (a.splice(0, 2) while a.length)
[ [ 1, 2 ],
  [ 3, 4 ],
  [ 5, 6 ],
  [ 7 ] ]

a.splice (0, 2) usuwa podtablicę [0..1] zi zwraca tablicę podrzędną a [0..1].
Tworzę

2
Polecam użycie nieniszczącej metody slice () zamiast splice ()
Ash Blue

7
results = []
chunk_size = 10
while(array.length > 0){
   results.push(array.splice(0, chunk_size))
}

1
Nie jestem pewien, dlaczego zostało to obniżone, ale kod mógłby posłużyć się pewnym wyjaśnieniem.
jpaugh

2
Ponieważ połączenie jest destrukcyjne w stosunku do oryginalnej tablicy.
metalim

7

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):

[11,22,33,44,55].map((_, i, all) => all.slice(2*i, 2*i+2)).filter(x=>x.length)

Jeśli chcesz zdefiniować dla tego funkcję, możesz to zrobić w następujący sposób (poprawa komentarza K. do odpowiedzi Blazemongera ):

const array_chunks = (array, chunk_size) => array
    .map((_, i, all) => all.slice(i*chunk_size, (i+1)*chunk_size))
    .filter(x => x.length)

6

I to byłby mój wkład w ten temat. Myślę, że .reduce()to najlepszy sposób.

var segment = (arr, n) => arr.reduce((r,e,i) => i%n ? (r[r.length-1].push(e), r)
                                                    : (r.push([e]), r), []),
        arr = Array.from({length: 31}).map((_,i) => i+1);
        res = segment(arr,7);
console.log(JSON.stringify(res));

Ale powyższa implementacja nie jest zbyt wydajna, ponieważ .reduce()przebiega przez wszystkie arrfunkcje. 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 rozmiar Math.ceil(arr/n);. Gdy mamy już pustą tablicę wyników, podobnie jak Array(Math.ceil(arr.length/n)).fill();reszta, należy zmapować na nią jej plasterki arr.

function chunk(arr,n){
  var r = Array(Math.ceil(arr.length/n)).fill();
  return r.map((e,i) => arr.slice(i*n, i*n+n));
}

arr = Array.from({length: 31},(_,i) => i+1);
res = chunk(arr,7);
console.log(JSON.stringify(res));



4

Aby uzyskać funkcjonalne rozwiązanie, używając Ramdy :

Gdzie popularProductsjest twoja tablica wejściowa, 5to wielkość porcji

import splitEvery from 'ramda/src/splitEvery'

splitEvery(5, popularProducts).map((chunk, i) => {
// do something with chunk

})


4

ES6 podejście jedna linia na podstawie Array.prototype reducei pushmetod:

const doChunk = (list, size) => list.reduce((r, v) =>
  (!r.length || r[r.length - 1].length === size ?
    r.push([v]) : r[r.length - 1].push(v)) && r
, []);

console.log(doChunk([0,1,2,3,4,5,6,7,8,9,10,11,12], 5));
// [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12]]

4

Wersja generatora ES6

function* chunkArray(array,size=1){
    var clone = array.slice(0);
    while (clone.length>0) 
      yield clone.splice(0,size); 
};
var a = new Array(100).fill().map((x,index)=>index);
for(const c of chunkArray(a,10)) 
    console.log(c);

Użyj constzamiast tego.
Константин Ван

4

To najbardziej wydajne i proste rozwiązanie, o jakim mogłem myśleć:

function chunk(array, chunkSize) {
    let chunkCount = Math.ceil(array.length / chunkSize);
    let chunks = new Array(chunkCount);
    for(let i = 0, j = 0, k = chunkSize; i < chunkCount; ++i) {
        chunks[i] = array.slice(j, k);
        j = k;
        k += chunkSize;
    }
    return chunks;
}

4

ES6 rozprzestrzenia funkcjonalne #ohmy #ftw

const chunk =
  (size, xs) => 
    xs.reduce(
      (segments, _, index) =>
        index % size === 0 
          ? [...segments, xs.slice(index, index + size)] 
          : segments, 
      []
    );

console.log( chunk(3, [1, 2, 3, 4, 5, 6, 7, 8]) );


4

Oto rekurencyjne rozwiązanie, które polega na optymalizacji ogonów.

const splitEvery = (n, xs, y=[]) =>
  xs.length===0 ? y : splitEvery(n, xs.slice(n), y.concat([xs.slice(0, n)])) 

console.log(splitEvery(2, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))


4

Jeszcze jedno rozwiązanie wykorzystujące arr.reduce():

const chunk = (arr, size) => (
  arr.reduce((acc, _, i) => {
    if (i % size === 0) acc.push(arr.slice(i, i + size))
    return acc
  }, [])
)

// Usage:
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const chunked = chunk(numbers, 3)
console.log(chunked)

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 sizewynosi zero, metoda zwraca pustą tablicę. Jeśli sizejest ujemny, metoda zwraca zepsute wyniki. Tak więc, jeśli zajdzie taka potrzeba, możesz zrobić coś z wartościami ujemnymi lub dodatnimi size.


Jeśli w twoim przypadku ważna jest prędkość, prosta forpętla byłaby szybsza niż używanie arr.reduce()(patrz test jsPerf ), a niektórzy mogą uważać ten styl za bardziej czytelny:

function chunk(arr, size) {
  // This prevents infinite loops
  if (size < 1) throw new Error('Size must be positive')

  const result = []
  for (let i = 0; i < arr.length; i += size) {
    result.push(arr.slice(i, i + size))
  }
  return result
}

2

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:

var longArray = ["Element 1","Element 2","Element 3", /*...*/];
var smallerArrays = []; // will contain the sub-arrays of 10 elements each
var arraySize = 10;
for (var i=0;i<Math.ceil(longArray.length/arraySize);i++) {
    smallerArrays.push(longArray.slice(i*arraySize,i*arraySize+arraySize));
}

Zmień wartość, arraySizeaby zmienić maksymalną długość mniejszych tablic.


2

Oto niemutujące rozwiązanie wykorzystujące tylko rekurencję i slice ().

const splitToChunks = (arr, chunkSize, acc = []) => (
    arr.length > chunkSize ?
        splitToChunks(
            arr.slice(chunkSize),
            chunkSize,
            [...acc, arr.slice(0, chunkSize)]
        ) :
        [...acc, arr]
);

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/

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.