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 a
aanzienlijk groter is dan b
(dwz een algoritme dat a
niet 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 .push
methode 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 .apply
in 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 b
te lang is (het probleem begint bij ongeveer 100.000 elementen, afhankelijk van de browser).
Als u niet kunt garanderen dat b
kort 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.concat
gebruiken.
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 .apply
kunnen 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 .apply
gebaseerde 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 forEach
gebaseerde methode hierboven kan profiteren van de schaarste en beter presteert dan de op .apply
gebaseerde 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.forEach
drie 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 forEach
als 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 arr2
niet 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 syntaxisa.push.apply(a, b)
– beperkt, snela = a.concat(b)
onbeperkt, langzaam alsa
groot isfor (let i in b) { a.push(b[i]); }
– onbeperkt, langzaam alsb
groot is
Elk fragment wijzigt a
om 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 b
zijn (de grootte van a
maakt niet uit).
Relevante MDN-documentatie: verspreide syntaxis, .apply(), .concat(), .push()
Overwegingen voor snelheid
Elke methode is snel als zowel a
als b
klein 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
→concat
is iets sneller dan een lus - je voegt veel elementen toe aan een kleine array
→concat
is veel sneller dan een lus
Dit verbaasde me: ik dacht dat a=a.concat(b)
een mooie memcpy van b
op a
zou 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 a
groot 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 Bsize
van 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 a
is. Een lus is dus vergelijkbaar met push(...b)
of push.apply
voor kleine b
s, maar zonder te breken als deze groot wordt ; echter, wanneer je de limiet nadert, is concat
weer 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.extend
in plaats van Array.push
voor > 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.