Een bestaande JavaScript-array uitbreiden met een andere array, zonder een nieuwe array te maken

Er lijkt geen manier te zijn om een bestaande JavaScript-array uit te breiden met een andere array, d.w.z. om de extend-methode van Python te emuleren.

Ik wil het volgende bereiken:

>>> a = [1, 2]
[1, 2]
>>> b = [3, 4, 5]
[3, 4, 5]
>>> SOMETHING HERE
>>> a
[1, 2, 3, 4, 5]

Ik weet dat er een a.concat(b)methode is, maar deze maakt een nieuwe array aan in plaats van simpelweg de eerste uit te breiden. Ik wil een algoritme dat efficiënt werkt wanneer aaanzienlijk groter is dan b(dwz een algoritme dat aniet kopieert).

Opmerking:dit is geen duplicaat van Hoe voeg je iets toe aan een array?— het doel hier is om de hele inhoud van de ene array aan de andere toe te voegen en dit “op zijn plaats” te doen, dwz zonder alle elementen van de uitgebreide array.


Antwoord 1, autoriteit 100%

De .pushmethode kan meerdere argumenten hebben. U kunt de spread-operatorgebruiken om alle de elementen van de tweede array als argumenten voor .push:

>>> a.push(...b)

Als uw browser ECMAScript 6 niet ondersteunt, kunt u .applyin plaats daarvan:

>>> a.push.apply(a, b)

Of misschien, als je denkt dat het duidelijker is:

>>> Array.prototype.push.apply(a,b)

Houd er rekening mee dat al deze oplossingen zullen mislukken met een stack overflow-fout als array bte lang is (het probleem begint bij ongeveer 100.000 elementen, afhankelijk van de browser).
Als u niet kunt garanderen dat bkort genoeg is, moet u een standaard op lus gebaseerde techniek gebruiken die in het andere antwoord wordt beschreven.


Antwoord 2, autoriteit 17%

Update 2018: Een beter antwoord is een nieuwere van mij: a.push(...b). Stem hier niet meer op, omdat het de vraag nooit echt beantwoordde, maar het was een hack uit 2015 rond first-hit-on-Google 🙂


Voor degenen die gewoon naar “JavaScript array extend” hebben gezocht en hier zijn terechtgekomen, kunnen ze heel goed Array.concatgebruiken.

var a = [1, 2, 3];
a = a.concat([5, 4, 3]);

Concat zal een kopie van de nieuwe array retourneren, zoals threadstarter niet wilde. Maar het kan je niet schelen (zeker voor de meeste soorten gebruik zal dit prima zijn).


Er is hier ook een mooie ECMAScript 6-suiker voor in de vorm van de spread-operator:

const a = [1, 2, 3];
const b = [...a, 5, 4, 3];

(Het kopieert ook.)


Antwoord 3, autoriteit 13%

U moet een op een lus gebaseerde techniek gebruiken. Andere antwoorden op deze pagina die zijn gebaseerd op het gebruik van .applykunnen mislukken voor grote arrays.

Een tamelijk beknopte op lus gebaseerde implementatie is:

Array.prototype.extend = function (other_array) {
    /* You should include a test to check whether other_array really is an array */
    other_array.forEach(function(v) {this.push(v)}, this);
}

Je kunt dan het volgende doen:

var a = [1,2,3];
var b = [5,4,3];
a.extend(b);

Het antwoord van DzinX(met push.apply) en andere op .applygebaseerde methoden mislukken wanneer de array die we toevoegen is groot (testen tonen aan dat groot voor mij > 150.000 items ongeveer is in Chrome en > 500.000 items in Firefox). U kunt deze fout zien optreden in deze jsperf.

Er treedt een fout op omdat de aanroepstack wordt overschreden wanneer ‘Function.prototype.apply’ wordt aangeroepen met een grote array als tweede argument. (MDN heeft een opmerking over de gevaren van het overschrijden van de call-stackgrootte met behulp van Function.prototype.apply– zie de sectie met de titel “apply en ingebouwde functies”.)

Voor een snelheidsvergelijking met andere antwoorden op deze pagina, bekijk deze jsperf(met dank aan EaterOfCode). De op een lus gebaseerde implementatie is qua snelheid vergelijkbaar met het gebruik van Array.push.apply, maar is meestal iets langzamer dan Array.slice.apply.

Interessant is dat als de array die u toevoegt schaars is, de op forEachgebaseerde methode hierboven kan profiteren van de schaarste en beter presteert dan de op .applygebaseerde methoden; bekijk deze jsperfals je dit zelf wilt testen.

Trouwens, laat je niet verleiden (zoals ik was!) om de forEach-implementatie verder in te korten tot:

Array.prototype.extend = function (array) {
    array.forEach(this.push, this);
}

omdat dit rommelresultaten oplevert! Waarom? Omdat Array.prototype.forEachdrie argumenten geeft aan de functie die het aanroept – dit zijn: (element_value, element_index, source_array). Deze worden allemaal naar uw eerste array gepusht voor elke iteratie van forEachals u “forEach(this.push, this)” gebruikt!


Antwoord 4, autoriteit 10%

Ik voel me tegenwoordig het meest elegant:

arr1.push(...arr2);

Het MDN-artikel over de spread-operatorvermeldt dit lekkere zoete manier in ES2015 (ES6):

Een betere duw

Voorbeeld: push wordt vaak gebruikt om een array naar het einde van een bestaande te pushen
reeks. In ES5 wordt dit vaak gedaan als:

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
// Append all items from arr2 onto arr1
Array.prototype.push.apply(arr1, arr2);

In ES6 met spreiding wordt dit:

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1.push(...arr2);

Houd er rekening mee dat arr2niet enorm kan zijn (houd deze onder ongeveer 100.000 items), omdat de oproepstack overloopt, volgens het antwoord van JCDUDE.


Antwoord 5, autoriteit 2%

Als je jQuery wilt gebruiken, is er $.merge()

Voorbeeld:

a = [1, 2];
b = [3, 4, 5];
$.merge(a,b);

Resultaat: a = [1, 2, 3, 4, 5]


Antwoord 6

Ik hou van de hierboven beschreven a.push.apply(a, b)-methode, en als je wilt, kun je altijd een bibliotheekfunctie als deze maken:

Array.prototype.append = function(array)
{
    this.push.apply(this, array)
}

en gebruik het zo

a = [1,2]
b = [3,4]
a.append(b)

Antwoord 7

Het is mogelijk om dit te doen met splice():

b.unshift(b.length)
b.unshift(a.length)
Array.prototype.splice.apply(a,b) 
b.shift() // Restore b
b.shift() // 

Maar ondanks dat het lelijker is, is het niet sneller dan push.apply, althans niet in Firefox 3.0.


Antwoord 8

Overzicht

  • a.push(...b)– beperkte, snelle, moderne syntaxis
  • a.push.apply(a, b)– beperkt, snel
  • a = a.concat(b)onbeperkt, langzaam als agroot is
  • for (let i in b) { a.push(b[i]); }– onbeperkt, langzaam als bgroot is

Elk fragment wijzigt aom te worden uitgebreid met b.

De “limited” snippets geven elk array-element door als argument, en het maximum aantal argumenten dat u aan een functie kunt doorgeven is beperkt . Uit die link blijkt dat a.push(...b)betrouwbaar is totdat er ongeveer 32k elementen in bzijn (de grootte van amaakt niet uit).

Relevante MDN-documentatie: verspreide syntaxis, .apply(), .concat(), .push()

Overwegingen voor snelheid

Elke methode is snel als zowel aals bklein zijn, dus in de meeste webapplicaties wil je push(...b)en klaar ermee.

Als je meer dan een paar duizend elementen verwerkt, hangt wat je wilt doen af van de situatie:

  • je voegt een paar elementen toe aan een grote array
    push(...b)is erg snel
  • je voegt veel elementen toe aan een grote array
    concatis iets sneller dan een lus
  • je voegt veel elementen toe aan een kleine array
    concatis veel sneller dan een lus

Dit verbaasde me: ik dacht dat a=a.concat(b)een mooie memcpy van bop azou kunnen doen zonder de moeite te nemen om individuele uitbreidingsbewerkingen uit te voeren zoals a.push(...b)zou moeten doen, dus altijd de snelste. In plaats daarvan is a.push(...b)veel, veel sneller, vooral wanneer agroot is.

De snelheid van verschillende methoden werd gemeten in Firefox 88 op Linux met:

a = [];
for (let i = 0; i < Asize; i++){
  a.push(i);
}
b = [];
for (let i = 0; i < Bsize; i++){
  b.push({something: i});
}
t=performance.now();
// Code to test
console.log(performance.now() - t);

Parameters en resultaten:

ms | Asize | Bsize | code
----+-------+-------+------------------------------
 ~0 |  any  |  any  | a.push(...b)
 ~0 |  any  |  any  | a.push.apply(a, b)
480 |   10M |    50 | a = a.concat(b)
  0 |   10M |    50 | for (let i in b) a.push(b[i])
506 |   10M |  500k | a = a.concat(b)
882 |   10M |  500k | for (let i in b) a.push(b[i])
 11 |    10 |  500k | a = a.concat(b)
851 |    10 |  500k | for (let i in b) a.push(b[i])

Merk op dat een Bsizevan 500 000 de grootste waarde is die door alle methoden op mijn systeem wordt geaccepteerd, daarom is deze kleiner dan Asize.

Alle tests zijn meerdere keren uitgevoerd om te zien of de resultaten uitbijters of representatief zijn. De snelle methoden zijn natuurlijk bijna onmeetbaar in slechts één run met behulp van performance.now(), maar aangezien de langzame methoden zo voor de hand liggend zijn en de twee snelle methoden beide volgens hetzelfde principe werken, moeten we’ doe niet de moeite om het een paar keer te herhalen om haren te splitsen.

De concat-methode is altijd traag als een van beide arrays groot is, maar de lus is alleen traag als deze veel functieaanroepen moet doen en het maakt niet uit hoe groot ais. Een lus is dus vergelijkbaar met push(...b)of push.applyvoor kleine bs, maar zonder te breken als deze groot wordt ; echter, wanneer je de limiet nadert, is concatweer een beetje sneller.


Antwoord 9

Deze oplossing werkt voor mij (met de spread-operator van ECMAScript 6):

let array = ['my', 'solution', 'works'];
let newArray = [];
let newArray2 = [];
newArray.push(...array); // Adding to same array
newArray2.push([...array]); // Adding as child/leaf/sub-array
console.log(newArray);
console.log(newArray2);

Antwoord 10

De antwoorden combineren…

Array.prototype.extend = function(array) {
    if (array.length < 150000) {
        this.push.apply(this, array)
    } else {
        for (var i = 0, len = array.length; i < len; ++i) {
            this.push(array[i]);
        };
    }  
}

Antwoord 11

Je kunt een polyfill maken voor extend zoals ik hieronder heb gedaan. Het wordt toegevoegd aan de array; in-place en zelf retourneren, zodat u andere methoden kunt koppelen.

if (Array.prototype.extend === undefined) {
  Array.prototype.extend = function(other) {
    this.push.apply(this, arguments.length > 1 ? arguments : other);
    return this;
  };
}
function print() {
  document.body.innerHTML += [].map.call(arguments, function(item) {
    return typeof item === 'object' ? JSON.stringify(item) : item;
  }).join(' ') + '\n';
}
document.body.innerHTML = '';
var a = [1, 2, 3];
var b = [4, 5, 6];
print('Concat');
print('(1)', a.concat(b));
print('(2)', a.concat(b));
print('(3)', a.concat(4, 5, 6));
print('\nExtend');
print('(1)', a.extend(b));
print('(2)', a.extend(b));
print('(3)', a.extend(4, 5, 6));
body {
  font-family: monospace;
  white-space: pre;
}

Antwoord 12

Een andere oplossing om meer dan twee arrays samen te voegen

var a = [1, 2],
    b = [3, 4, 5],
    c = [6, 7];
// Merge the contents of multiple arrays together into the first array
var mergeArrays = function() {
 var i, len = arguments.length;
 if (len > 1) {
  for (i = 1; i < len; i++) {
    arguments[0].push.apply(arguments[0], arguments[i]);
  }
 }
};

Vervolgens bellen en afdrukken als:

mergeArrays(a, b, c);
console.log(a)

Uitvoer zal zijn: Array [1, 2, 3, 4, 5, 6, 7]


Antwoord 13

Het antwoord is supereenvoudig.

>>> a = [1, 2]
[1, 2]
>>> b = [3, 4, 5]
[3, 4, 5]
>>> SOMETHING HERE
(The following code will combine the two arrays.)
a = a.concat(b);
>>> a
[1, 2, 3, 4, 5]

Concat werkt op dezelfde manier als samenvoeging van JavaScript-tekenreeksen. Het retourneert een combinatie van de parameter die u in de concat-functie hebt geplaatst aan het einde van de array waarop u de functie aanroept. De crux is dat je de geretourneerde waarde aan een variabele moet toewijzen, anders gaat deze verloren. Dus bijvoorbeeld

a.concat(b);  <--- This does absolutely nothing since it is just returning the combined arrays, but it doesn't do anything with it.

Antwoord 14

Een andere optie, als je lodash hebt geïnstalleerd:

import { merge } from 'lodash';
 var arr1 = merge(arr1, arr2);

Antwoord 15

Gebruik Array.extendin plaats van Array.pushvoor > 150.000 records.

if (!Array.prototype.extend) {
  Array.prototype.extend = function(arr) {
    if (!Array.isArray(arr)) {
      return this;
    }
    for (let record of arr) {
      this.push(record);
    }
    return this;
  };
}

Antwoord 16

Je kunt dat doen door simpelweg nieuwe elementen aan de array toe te voegen met behulp van de push()methode.

let colors = ["Red", "Blue", "Orange"];
console.log('Array before push: ' + colors);
// append new value to the array
colors.push("Green");
console.log('Array after push : ' + colors);

Antwoord 17

Super simpel, rekent niet op spread operators of solliciteert, als dat een probleem is.

b.map(x => a.push(x));

Na wat prestatietests hierop te hebben uitgevoerd, is het vreselijk traag, maar het beantwoordt de vraag met betrekking tot het niet maken van een nieuwe array. Concat is aanzienlijk sneller, zelfs jQuery’s $.merge()oeps.

https://jsperf.com/merge-arrays19b/1

Other episodes