1046 Stimmen

Array in Stücke aufteilen

Nehmen wir an, dass ich ein Javascript-Array habe, das wie folgt aussieht:

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

Welcher Ansatz wäre angemessen, um das Array in viele kleinere Arrays mit, sagen wir, höchstens 10 Elementen aufzuteilen?

1335voto

Blazemonger Punkte 85817

El array.slice() Methode kann ein Stück vom Anfang, der Mitte oder dem Ende eines Arrays für beliebige Zwecke extrahieren, ohne das ursprüngliche Array zu verändern.

const chunkSize = 10;
for (let i = 0; i < array.length; i += chunkSize) {
    const chunk = array.slice(i, i + chunkSize);
    // do whatever
}

Die letzte chunk kann kleiner sein als chunkSize . Zum Beispiel, wenn man eine array von 12 Elementen hat der erste Chunk 10 Elemente, der zweite Chunk hat nur 2.

Beachten Sie, dass eine chunkSize0 wird eine Endlosschleife verursachen.

315voto

Andrei R Punkte 4140

Hier ist eine ES6-Version mit reduce

const perChunk = 2 // items per chunk    

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

const 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 schon sind Sie bereit, weitere Map/Reduce-Transformationen zu verketten. Ihr Eingabe-Array bleibt intakt


Wenn Sie eine kürzere, aber weniger lesbare Version bevorzugen, können Sie einige concat in den Mix einbringen, um das gleiche Ergebnis zu erzielen:

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

Sie können den Restwertoperator verwenden, um aufeinanderfolgende Elemente in verschiedene Abschnitte zu unterteilen:

const ch = (i % perChunk);

187voto

Ikechukwu Eze Punkte 2217

Verwendung von Generatoren

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

Kann mit Typescript wie folgt eingegeben werden:

function* chunks<T>(arr: T[], n: number): Generator<T[], void> {
  for (let i = 0; i < arr.length; i += n) {
    yield arr.slice(i, i + n);
  }
}

175voto

ninjagecko Punkte 82995

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

kleine Ergänzung :

Ich sollte darauf hinweisen, dass die obige Vorgehensweise eine (meiner Meinung nach) nicht sehr elegante Umgehung der Verwendung von Array.map . Es tut im Wesentlichen das Folgende, wobei ~ für Verkettung steht:

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

Sie hat die gleiche asymptotische Laufzeit wie die nachstehende Methode, aber vielleicht einen schlechteren konstanten Faktor aufgrund der Erstellung leerer Listen. Man könnte dies wie folgt umschreiben (größtenteils das gleiche wie Blazemongers Methode, weshalb ich diese Antwort ursprünglich nicht eingereicht habe):

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

Heutzutage bevorzuge ich die obige Methode oder eine der folgenden Möglichkeiten:

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 wollen, ist es eigentlich nur ein Einzeiler (ohne den Schnickschnack):

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

o

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

151voto

furf Punkte 2579

Versuchen Sie zu vermeiden, mit nativen Prototypen zu hantieren, einschließlich Array.prototype wenn Sie nicht wissen, wer Ihren Code verwenden wird (Dritte, Mitarbeiter, Sie selbst zu einem späteren Zeitpunkt, usw.).

Es gibt Möglichkeiten, Prototypen sicher zu erweitern (aber nicht in allen Browsern), und es gibt Möglichkeiten, aus erweiterten Prototypen erstellte Objekte sicher zu konsumieren, aber eine bessere Faustregel ist es, die Das Prinzip der geringsten Überraschung und diese Praktiken gänzlich zu vermeiden.

Wenn Sie etwas Zeit haben, sehen Sie sich den Vortrag von Andrew Dupont auf der JSConf 2011 an, "Alles ist erlaubt: Ausbau von Einbauten" für eine gute Diskussion über dieses Thema.

Aber zurück zur Frage: Die oben genannten Lösungen funktionieren zwar, sind aber übermäßig komplex und erfordern einen 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;

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X