Array in Blöcke aufteilen

516

Angenommen, ich habe ein Javascript-Array, das wie folgt aussieht:

["Element 1","Element 2","Element 3",...]; // with close to a hundred elements.

Welcher Ansatz wäre geeignet, um das Array in viele kleinere Arrays mit beispielsweise höchstens 10 Elementen aufzuteilen (aufzuteilen)?

Industriell
quelle
Mögliches Duplikat der Aufteilung eines JS-Arrays in N Arrays
TJ
24
Für lodash- Benutzer suchen Sie nach _.chunk .
Ulysse BN
2
Ich habe gerade versucht, const chunk = (arr, n) => arr.length ? [arr.slice(0, n), ...chunk(arr.slice(n), n)] : []was nett und kurz ist, aber ungefähr 256 × so lange zu dauern scheint wie @ AymKdns Antwort für 1.000 Elemente und 1.058 × so lange für 10.000 Elemente!
Toph
Wenn Sie auch die Mindestgröße des letzten Blocks
Ahmet Cetin

Antworten:

640

Die array.slice- Methode kann ein Slice vom Anfang, der Mitte oder dem Ende eines Arrays für beliebige Zwecke extrahieren, ohne das ursprüngliche Array zu ändern.

var i,j,temparray,chunk = 10;
for (i=0,j=array.length; i<j; i+=chunk) {
    temparray = array.slice(i,i+chunk);
    // do whatever
}
Blazemonger
quelle
19
Denken Sie daran, ob dies eine nützliche Funktion ist, um sich gegen das chunkSein zu behaupten 0. (Endlosschleife)
Steven Lu
19
Nein, der letzte Teil sollte nur kleiner sein als die anderen.
Blazemonger
7
@Blazemonger, in der Tat! Das nächste Mal werde ich es selbst versuchen, bevor ich zu Schlussfolgerungen komme. Ich habe (fälschlicherweise) angenommen, dass das Übergeben einer Eingabe in array.slice, die die Grenzen des Arrays überschreitet, ein Problem darstellt, aber es funktioniert einfach perfekt!
Rysqui
55
Für Einzeiler (Kettenliebhaber) : 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));.
4онстантин Ван
8
Warum brauchst du j? Zuerst dachte ich, es sei eine Optimierung, aber sie ist tatsächlich langsamer als für (i = 0; i <array.length; i ++) {}
Alex
149

Geändert von einer Antwort von 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]]


kleiner Nachtrag :

Ich sollte darauf hinweisen, dass das oben Genannte eine meiner Meinung nach nicht so elegante Problemumgehung ist Array.map. Grundsätzlich wird Folgendes ausgeführt, wobei ~ Verkettung ist:

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

Es hat die gleiche asymptotische Laufzeit wie die folgende Methode, aber möglicherweise einen schlechteren konstanten Faktor, da leere Listen erstellt werden. Man könnte dies wie folgt umschreiben (meistens das gleiche wie Blazemongers Methode, weshalb ich diese Antwort ursprünglich nicht eingereicht habe):

Effizientere Methode:

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


Mein bevorzugter Weg ist heutzutage der oben genannte oder einer der folgenden:

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

  }
});

Demo:

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

Oder wenn Sie keine Array.range-Funktion möchten, handelt es sich tatsächlich nur um einen Einzeiler (ohne Flusen):

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));
}});

oder

Object.defineProperty(Array.prototype, 'chunk', {value: function(n) {
    return Array.from(Array(ceil(this.length/n)), (_,i)=>this.slice(i*n,i*n+n));
}});
Ninjagecko
quelle
41
Eh, ich würde es vermeiden, mit dem Prototyp herumzuspielen, da das Gefühl der Coolness, das Sie durch das Aufrufen der chunkFunktion auf dem Array erhalten, die zusätzliche Komplexität, die Sie hinzufügen, und die subtilen Fehler, die das Durcheinander mit integrierten Prototypen verursachen kann, nicht wirklich überwiegt.
Gordon Gustafson
12
Er spielt nicht mit ihnen, er erweitert sie für Arrays. Ich verstehe, dass ich Object.prototype niemals berühre, da dies zu allen Objekten (allem) führen würde, aber für diese Array-spezifische Funktion sehe ich keine Probleme.
Rlemon
Ziemlich sicher , dass sollte array.map(function(i)nicht array.map(function(elem,i)obwohl
Nigel Engel
3
Basierend auf der Kompatibilitätstabelle auf der Mozilla Dev-Site Array.map für IE9 +. Achtung.
Maikel D
Achten Sie darauf, schwebend zu übergeben - 7,5 Arten von Zahlen - führen zu unvorhersehbaren Brocken
Gal Bracha
103

Hier ist eine ES6-Version mit Reduce

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']]

Und Sie sind bereit, weitere Karten- / Reduktions-Transformationen zu verketten. Ihr Eingabearray bleibt erhalten


Wenn Sie eine kürzere, aber weniger lesbare Version bevorzugen, können Sie einige davon concatin die Mischung streuen, um das gleiche Endergebnis zu erzielen:

inputArray.reduce((all,one,i) => {
   const ch = Math.floor(i/perChunk); 
   all[ch] = [].concat((all[ch]||[]),one); 
   return all
}, [])
Andrei R.
quelle
Dies scheint die am stärksten kondensierte Lösung zu sein. Was bekommt chunkIndex = Math.floor (index / perChunk)? Ist es der Durchschnitt?
Ich-Ich
5/2 = 2.5und Math.floor(2.5) = 2so wird Gegenstand mit Index 5 in Eimer 2 gelegt
Andrei R
Danke Andrei R. Ah, also geht es durch 0 - 2. Wie heißt das also in mathematischen Begriffen? Ich denke, mein Punkt ist, ich hätte nie gedacht, Index / 2 für jeden Index zu teilen, um den Index für jedes Slice zu erhalten. Also versuche ich, meinen Kopf darum zu wickeln, weil ich es wirklich mag, es aber in mathematischen Begriffen nicht vollständig verstehe. Normalerweise mache ich das, um Durchschnittswerte einer Gesamtzahl zu erhalten, aber das sieht anders aus.
Ich-Ich
Diese Lösung ist im Vergleich zu anderen Lösungen ineffizient, da Sie jedes Element durchlaufen müssen.
JP de la Torre
Sie haben Recht @JPdelaTorre, es ist wahrscheinlich nicht so effizient wie Lösungen, die Arrays schneiden, aber Sie spalten hier Haare. Die meisten aufgeführten Antworten wären nach dieser Definition ineffizient.
Andrei R
102

Vermeiden Sie es, mit nativen Prototypen, einschließlich Array.prototype, herumzuspielen, wenn Sie nicht wissen, wer Ihren Code verbraucht (Dritte, Mitarbeiter, Sie selbst zu einem späteren Zeitpunkt usw.).

Es gibt Möglichkeiten, Prototypen sicher zu erweitern (jedoch nicht in allen Browsern), und es gibt Möglichkeiten, Objekte, die aus erweiterten Prototypen erstellt wurden, sicher zu konsumieren. Eine bessere Faustregel besteht jedoch darin, dem Prinzip der geringsten Überraschung zu folgen und diese Praktiken insgesamt zu vermeiden.

Wenn Sie etwas Zeit haben, lesen Sie Andrew Duponts JSConf 2011-Vortrag "Alles ist erlaubt: Erweitern von integrierten Funktionen" , um eine gute Diskussion zu diesem Thema zu erhalten.

Zurück zur Frage: Die oben genannten Lösungen funktionieren zwar, sind jedoch zu komplex und erfordern unnötigen Rechenaufwand. Hier ist meine Lösung:

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;
Rasen
quelle
25
"Vermeiden Sie es, mit nativen Prototypen herumzuspielen" Neue JS-Entwickler sollten eine vorübergehende, wenn nicht dauerhafte Tätowierung dieses Satzes erhalten.
Jacob Dalton
2
Ich benutze Javascript seit Jahren und verbringe so gut wie keine Zeit damit, mich mit Prototypen zu beschäftigen, höchstens mit den meisten aufrufenden Funktionen, und nie so zu modifizieren, wie es manche Leute sehen.
1984
2
Der beste Vorschlag in meinen Augen, der am einfachsten zu verstehende und in der Umsetzung, vielen Dank!
Olga Farber
1
@ JacobDalton Ich denke, es ist alles die Schuld der Universitäten. Die Leute denken, OOP muss überall verwendet werden. Sie haben also Angst davor, "nur eine Funktion zu erstellen". Sie wollen sicher sein, dass sie es in etwas stecken. Auch wenn es überhaupt nicht angebracht ist. Wenn es keine Punktnotation gibt, gibt es keine "Architektur".
Gherman
51

Ich habe die verschiedenen Antworten auf jsperf.com getestet. Das Ergebnis ist dort verfügbar: https://web.archive.org/web/20150909134228/https://jsperf.com/chunk-mtds

Und die schnellste Funktion (und das funktioniert ab IE8) ist diese:

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;
}
AymKdn
quelle
1
Vielen Dank an @AymKdn für diesen Benchmark: Das war so hilfreich! Ich habe den Splice-Ansatz verwendet und mein Chrome v70-Browser mit einer Blockgröße von 884432 zum Absturz gebracht. Mit Ihrem vorgeschlagenen "Slice" -Ansatz stürzt mein Code den "Render" -Prozess des Browsers nicht mehr ab. :)
Benny Neugebauer
34

Ich würde lieber die Spleißmethode verwenden:

var chunks = function(array, size) {
  var results = [];
  while (array.length) {
    results.push(array.splice(0, size));
  }
  return results;
};
Arek Flinik
quelle
16
Bei dieser Spleißlösung muss vorsichtig vorgegangen werden, da sie das ursprüngliche Array ändert. Dokumentieren Sie daher die Nebenwirkungen klar und deutlich.
Bdrx
3
Verwenden Sie stattdessen Slice
Mplungjan
@mplungjan Das Ergebnis wäre bei Verwendung von Slice immer wieder dasselbe Array. Es handelt sich also nicht wirklich um einen Ersatz ohne weitere Modifikationen.
Nietonfir
Das einzige, was ich zu dieser Antwort hinzufügen würde, ist ein Klon zum ursprünglichen Array. Ich würde das mit dem Spread-Operator von ES6 machen. var clone = [...array]Führen Sie dann die Längenprüfung durch und spleißen Sie über das geklonte Array.
MauricioLeal
1
Wenn Sie keine ES6-Funktionen verwenden können, können Sie einfach array = array.slice()eine flache Kopie erstellen.
3limin4t0r
34

Einzeiler in 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))
Shairon Toledo
quelle
9
es ändert das ursprüngliche listArray
Jacka
6
Einfache Lösung mit .slice () .. .map((_,i) => list.slice(i*chuckSize,i*chuckSize+chuckSize))
James Robey
2
Unter JSPerf ist dies drastisch leistungsfähiger als viele der anderen Antworten.
Micah Henning
30

Alte Frage: Neue Antwort! Ich habe tatsächlich mit einer Antwort auf diese Frage gearbeitet und einen Freund dazu gebracht, sie zu verbessern! Hier ist es also:

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]]
rlemon
quelle
14
Zu Ihrer Information, die Leistung dieser Methode ist O (N ^ 2), daher sollte sie nicht in leistungskritischen Codeabschnitten oder mit langen Arrays verwendet werden (insbesondere wenn das Array .lengthviel größer als die Blockgröße ist n). Wenn dies eine faule Sprache wäre (im Gegensatz zu Javascript), würde dieser Algorithmus nicht unter O (N ^ 2) -Zeit leiden. Die rekursive Implementierung ist jedoch elegant. Sie können es wahrscheinlich ändern, um die Leistung zu verbessern, indem Sie zuerst eine wiederkehrende Hilfsfunktion definieren array,positionund dann Folgendes auslösen : Gibt Array.prototype.chunk[Ihre
Hilfsfunktion
danke sensai, dass du mich mit deinem geist gesegnet hast, den ich heute abend gechannelt haben muss
gebissen
1
oder ...var chunk = (arr, n) => { if ( !arr.length ) return []; return [ arr.slice( 0, n ) ].concat( chunk(arr.slice(n), n) ) }
Ed Williams
28

Heutzutage können Sie die Chunk-Funktion von lodash verwenden, um das Array in kleinere Arrays aufzuteilen. Https://lodash.com/docs#chunk Sie müssen nicht mehr mit den Schleifen herumspielen!

George Herolyants
quelle
3
Ich denke, es sollte einen Haftungsausschluss für SO-Javascript-Fragen geben: Haben Sie Lodash ausprobiert? So ziemlich das erste, was ich in den Knoten oder den Browser einbinde.
Jacob Dalton
20

Es gab viele Antworten, aber das ist, was ich benutze:

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]]

Suchen Sie zunächst nach einem Rest, wenn Sie den Index durch die Blockgröße dividieren.

Wenn es einen Rest gibt, geben Sie einfach das Akkumulator-Array zurück.

Wenn es keinen Rest gibt, ist der Index durch die Blockgröße teilbar. Nehmen Sie also einen Slice aus dem ursprünglichen Array (beginnend mit dem aktuellen Index) und fügen Sie ihn dem Akkumulator-Array hinzu.

Das zurückgegebene Akkumulator-Array für jede Iteration von Reduce sieht also ungefähr so ​​aus:

// 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]]
Steve Holgado
quelle
Schöne Lösung und schöne visuelle Darstellung der Iterationen. Am
mts knn
15

Generatoren verwenden

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]]

Ikechukwu Eze
quelle
3
Ich war überrascht über all die Antworten auf diese Frage, bei denen fast Generatoren verwendet wurden oder die auf kompliziertere Weise verwendet wurden. Schöne Kürze und Leistung mit dieser Lösung.
Keith
14

Ok, fangen wir mit einem ziemlich engen an:

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); });
}

Welches wird so verwendet:

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

Dann haben wir diese enge Reduzierfunktion:

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

Welches wird so verwendet:

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

Da ein Kätzchen stirbt, wenn wir uns thisan eine Zahl binden , können wir stattdessen wie folgt manuell curryen:

// 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;
   };
}

Welches wird so verwendet:

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

Dann die immer noch ziemlich enge Funktion, die alles auf einmal erledigt:

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);
user239558
quelle
Funktioniert nicht in iE8.
Nadeemmnn Mohd
4
HA! Ich liebe den Kätzchen Kommentar. Entschuldigung für keine zusätzlichen konstruktiven Eingaben :)
29er
Ich würde tun (p[i/n|0] || (p[i/n|0] = [])), damit Sie keinen Wert zuweisen, wenn nicht notwendig ...
yckart
12

Ich wollte eine einfache, nicht mutierende Lösung in reinem ES6 erstellen. Besonderheiten in Javascript machen es erforderlich, das leere Array vor dem Mapping zu füllen :-(

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

Diese Version mit Rekursion scheint einfacher und überzeugender zu sein:

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

Die lächerlich schwachen Array-Funktionen von ES6 sorgen für gute Rätsel :-)

thoredge
quelle
1
Ich habe meine auch so geschrieben. Es funktioniert immer noch, wenn Sie das 0aus dem entfernen fill, was das fillAussehen ein wenig vernünftiger macht, imho.
Coert Grobbelaar
12

Ich denke, dies ist eine schöne rekursive Lösung mit ES6-Syntax:

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

Gergely Fehérvári
quelle
9

Erstellt ein npm-Paket für dieses 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;

Bei Verwendung eines TypedArray

var result = [];

for (var i = 0; i < arr.length; i += size) {
  result.push(arr.subarray(i, size + i));
}
return result;
zhiyelee
quelle
@ A1rPun Mein schlechtes, ich habe dort keinen Kommentar hinzugefügt. Ja, es gibt keine sliceMethode für TypedArray, wir können subarraystattdessen developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
zhiyelee
8

Wenn Sie die EcmaScript-Version> = 5.1 verwenden, können Sie eine funktionale Version der chunk()Verwendung von array.reduce () mit O (N) -Komplexität implementieren :

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' ] ]

Erklärung von jedem // nbroben:

  1. Erstellen Sie einen neuen Block, wenn der vorherige Wert, dh das zuvor zurückgegebene Array von Blöcken, leer ist oder wenn der letzte vorherige Block vorhanden ist chunkSize Elemente enthält
  2. Fügen Sie den neuen Block zum Array der vorhandenen Blöcke hinzu
  3. Andernfalls ist der aktuelle Block der letzte Block im Array von Blöcken
  4. Fügen Sie dem Block den aktuellen Wert hinzu
  5. Gibt das geänderte Array von Chunks zurück
  6. Initialisieren Sie die Reduzierung, indem Sie ein leeres Array übergeben

Currying basierend auf chunkSize:

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

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

Sie können die chunk()Funktion zum globalen ArrayObjekt hinzufügen :

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' ] ]

matsev
quelle
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 ] ]
Arpit Jain
quelle
a.splice (0, 2) entfernt das Subarray von a [0..1] aus a und gibt das Subarray a [0..1] zurück. Ich mache ein Array von all diesen Arrays
Arpit Jain
2
Ich empfehle die zerstörungsfreie Slice () -Methode anstelle von splice ()
Ash Blue
7
results = []
chunk_size = 10
while(array.length > 0){
   results.push(array.splice(0, chunk_size))
}
Jon
quelle
1
Ich bin mir nicht sicher, warum dies abgelehnt wurde, aber der Code könnte eine Erklärung gebrauchen.
Jpaugh
2
Weil Spleiß das ursprüngliche Array zerstört.
Metalim
7

Der folgende ES2015-Ansatz funktioniert ohne Definition einer Funktion und direkt auf anonymen Arrays (Beispiel mit Blockgröße 2):

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

Wenn Sie eine Funktion dafür definieren möchten, können Sie dies wie folgt tun (Verbesserung des Kommentars von K ._ zu Blazemongers Antwort ):

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

Und das wäre mein Beitrag zu diesem Thema. Ich denke, das .reduce()ist der beste Weg.

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

Die obige Implementierung ist jedoch nicht sehr effizient, da .reduce()sie alle arrFunktionen durchläuft . Ein effizienterer Ansatz (der der schnellsten imperativen Lösung sehr nahe kommt) wäre, über das reduzierte (zu zerteilende) Array zu iterieren, da wir seine Größe im Voraus durch berechnen können Math.ceil(arr/n);. Sobald wir das leere Ergebnisarray haben, müssen wir wie Array(Math.ceil(arr.length/n)).fill();der Rest Slices des arrArrays darauf abbilden .

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

Reduzieren
quelle
5

Verwenden Sie ein Stück von lodash

lodash.chunk(arr,<size>).forEach(chunk=>{
  console.log(chunk);
})
Milind Chaudhary
quelle
5

Eine weitere Lösung mit 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)

Diese Lösung ist der Lösung von Steve Holgado sehr ähnlich . Da diese Lösung jedoch keine Array-Streuung verwendet und keine neuen Arrays in der Reduzierungsfunktion erstellt, ist sie schneller (siehe jsPerf-Test ) und subjektiv besser lesbar (einfachere Syntax) als die andere Lösung.

Bei jeder n-ten Iteration (wobei n = size; beginnend mit der ersten Iteration) wird dem Akkumulatorarray ( acc) ein Teil des Arrays angehängt (arr.slice(i, i + size) ) und dann zurückgegeben. Bei anderen Iterationen wird das Akkumulatorarray unverändert zurückgegeben.

Wenn sizeNull ist, gibt die Methode ein leeres Array zurück. Wenn dies sizenegativ ist, gibt die Methode fehlerhafte Ergebnisse zurück. Wenn dies in Ihrem Fall erforderlich ist, möchten Sie möglicherweise etwas gegen negative oder nicht positive sizeWerte unternehmen .


Wenn in Ihrem Fall die Geschwindigkeit wichtig ist, ist eine einfache forSchleife schneller als die Verwendung arr.reduce()(siehe jsPerf-Test ), und einige finden diesen Stil möglicherweise auch besser lesbar:

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
}
mts knn
quelle
4

Für eine funktionale Lösung mit Ramda :

Wo popularProductsist Ihr Eingabearray, 5ist die Blockgröße

import splitEvery from 'ramda/src/splitEvery'

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

})

Damian Green
quelle
4

Einzeiliger ES6-Ansatz basierend auf Array.prototype reduceund pushMethoden:

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]]
Dhilt
quelle
4

ES6 Generator Version

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);
Rm558
quelle
Verwenden Sie conststattdessen.
4онстантин Ван
4

Dies ist die effizienteste und einfachste Lösung, die ich mir vorstellen kann:

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;
}
mpen
quelle
4

ES6 verbreitet funktionale #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]) );

goofballLogic
quelle
4

Hier ist eine rekursive Lösung, die Tail Call Optimize ist.

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

Chris Vouga
quelle
2

BEARBEITEN: @ mblase75 hat der früheren Antwort während des Schreibens meiner Antwort einen präziseren Code hinzugefügt, daher empfehle ich, mit seiner Lösung fortzufahren.

Sie könnten folgenden Code verwenden:

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));
}

Ändern Sie den Wert von arraySize, um die maximale Länge der kleineren Arrays zu ändern.

dentaku
quelle
2

Hier ist eine nicht mutierende Lösung, die nur Rekursion und Slice () verwendet.

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

Dann benutze es einfach gerne um splitToChunks([1, 2, 3, 4, 5], 3)zu bekommen [[1, 2, 3], [4, 5]].

Hier ist eine Geige, die Sie ausprobieren können: https://jsfiddle.net/6wtrbx6k/2/

Suhair Zain
quelle