Mevcut bir JavaScript dizisini yeni bir dizi oluşturmadan başka bir diziyle genişletme


971

Mevcut bir JavaScript dizisini başka bir diziyle genişletmenin, yani Python'un extendyöntemini taklit etmenin bir yolu yok gibi görünüyor .

Aşağıdakileri başarmak istiyorum:

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

Bir a.concat(b)yöntem olduğunu biliyorum , ama sadece birincisini genişletmek yerine yeni bir dizi oluşturur. Ne zaman aönemli ölçüde daha büyük b(yani bir kopyalama değil a) verimli çalışan bir algoritma istiyorum .

Not: Bu kopyası olmaması bir diziye şey eklemek nasıl? - Buradaki amaç, bir dizinin tüm içeriğini diğerine eklemek ve "yerinde", yani genişletilmiş dizinin tüm öğelerini kopyalamadan yapmaktır.


5
Bir cevap üzerinde diş fırçası yorumuna @ Gönderen: a.push(...b). Kavram olarak üstteki cevaba benzer, ancak ES6 için güncellendi.
tscizzle

Yanıtlar:


1498

.pushYöntem birden fazla argüman alabilir. İkinci dizinin tüm öğelerini bağımsız değişken olarak iletmek için yayma işlecini kullanabilirsiniz .push:

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

Tarayıcınız ECMAScript 6'yı desteklemiyorsa, .applybunun yerine şunları kullanabilirsiniz :

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

Ya da belki de daha net olduğunu düşünüyorsanız:

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

Dizi bçok uzunsa , tüm bu çözümlerin yığın taşması hatasıyla başarısız olacağını unutmayın (sorun tarayıcıya bağlı olarak yaklaşık 100.000 öğede başlar).
Bunun byeterince kısa olduğunu garanti edemiyorsanız , diğer cevapta açıklanan standart döngü tabanlı bir teknik kullanmalısınız.


3
Bence bu senin en iyi bahsin. Başka bir şey, yineleme veya başka bir uygulama çabası içerecektir ()
Peter Bailey

44
"B" (genişletilecek dizi) büyükse (testlerime göre Chrome'da yaklaşık 150000 giriş) bu yanıt başarısız olur. For döngüsü kullanmalı veya "b" üzerindeki dahili "forEach" işlevini daha iyi kullanmalısınız. Cevabımı gör: stackoverflow.com/questions/1374126/…
jcdude

35
@Deqing: Array'ın pushyöntemi, daha sonra dizinin arkasına itilen herhangi bir sayıda argüman alabilir. Bu a.push('x', 'y', 'z'), a3 elemanlı geçerli bir çağrıdır . applydiziyi alan ve öğelerin tümünü işleve açık bir şekilde konumsal elemanlar olarak verilmiş gibi kullanan herhangi bir fonksiyonun yöntemidir. Yani a.push.apply(a, ['x', 'y', 'z'])diziyi 3 elemanla genişletirdi. Ayrıca, applyilk argüman olarak bir bağlam alır ( aeklenmek için oradan tekrar geçeriz a).
DzinX

Not: bu senaryoda, ilk dönüş değeri [1,2,3,4,5] değil, 5 iken ise == [1,2,3,4,5].
jawo

1
Bir diğer biraz daha az kafa karıştırıcı (ve çok daha uzun) çağırma şöyle olacaktır: [].push.apply(a, b).
niry

259

Güncelleme 2018 : Daha iyi bir cevap benim bir yeni biridir : a.push(...b). Soruyu asla gerçekten cevaplamadığı için artık bunu oylamayın, ancak Google'da ilk hit olan 2015'teki bir hackti :)


Basitçe "JavaScript dizi genişletmek" için arama yaptı ve buraya var olanlar için, çok iyi kullanabilirsiniz Array.concat.

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

Concat, iş parçacığı başlatıcısı istemediği için yeni dizinin bir kopyasını döndürür. Ama umursamayabilirsiniz (kesinlikle çoğu kullanım için bu iyi olacaktır).


Bunun için forma operatörü şeklinde güzel ECMAScript 6 şekeri de var:

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

(Ayrıca kopyalar.)


43
Soru açıkça belirtiyordu: "yeni bir dizi oluşturmadan?"
Wilt

10
@Wilt: Bu doğrudur, cevap neden olsa diyor :) Bu, Google'da ilk kez uzun zamandır vuruldu. Ayrıca diğer çözümler çirkindi, ideolojik ve hoş bir şey istediler. Her ne kadar günümüzde arr.push(...arr2)daha yeni ve daha iyi ve bu soruya Teknik Olarak Doğru (tm) bir cevap.
odinho - Velmont

7
Seni duyuyorum, ama ben yeni bir dizi oluşturmadan "javascript append dizi" aradı , o zaman yüksek bir 60 kat upvoted cevap gerçek soruya cevap vermediğini görmek üzücü;) Ben aşağı oy vermedi, çünkü cevabınızda bunu netleştirdiniz.
Wilt

202

Döngü tabanlı bir teknik kullanmalısınız. Bu sayfada, kullanımına dayalı diğer yanıtlar .applybüyük diziler için başarısız olabilir.

Oldukça kısa döngü tabanlı bir uygulama:

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

Ardından aşağıdakileri yapabilirsiniz:

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

Eklediğimiz.apply dizi büyük olduğunda DzinX'in yanıtı (push.apply kullanarak) ve diğer temel yöntemler başarısız olur (testler benim için büyük olanın Chrome'da yaklaşık 150.000 giriş ve Firefox'ta> 500.000 giriş olduğunu gösteriyor). Bu hatayı bu jsperf dosyasında görebilirsiniz .

İkinci bağımsız değişken olarak büyük bir dizi ile 'Function.prototype.apply' çağrıldığında çağrı yığını boyutu aşıldığı için bir hata oluşur. (MDN, Function.prototype.apply komutunu kullanarak çağrı yığını boyutunu aşmanın tehlikeleri hakkında bir not içerir - "Uygula ve yerleşik işlevleri" başlıklı bölüme bakın.)

Bu sayfadaki diğer cevaplarla hız karşılaştırması için bu jsperf'e ( EaterOfCode sayesinde) göz atın. Döngü tabanlı uygulama, kullanım hızına benzer Array.push.apply, ancak biraz daha yavaş olma eğilimindedir Array.slice.apply.

İlginç bir şekilde, eklediğiniz dizi seyrek ise, forEachyukarıdaki temel yöntem seyreklikten yararlanabilir ve .applytemel yöntemlerden daha iyi performans gösterebilir ; Bunu kendiniz test etmek istiyorsanız bu jsperf'e göz atın .

Bu arada, (olduğu gibi!) ForEach uygulamasını daha da kısaltmak için cazip olmayın:

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

çünkü bu çöp sonuçları üretir! Neden? Çünkü Array.prototype.forEachçağırdığı işleve üç argüman sağlar - bunlar: (element_value, element_index, source_array). Tüm bunlar, forEach"forEach (this.push, this)" kullanırsanız, her yineleme için ilk dizinize aktarılacaktır!


1
ps other_array'ın gerçekten bir dizi olup olmadığını test etmek için, burada açıklanan seçeneklerden birini seçin: stackoverflow.com/questions/767486/…
jcdude

6
İyi cevap. Bu sorunun farkında değildim. Cevabınızı burada alıntıladım: stackoverflow.com/a/4156156/96100
Tim Down

2
.push.applyaslında .forEachv8'den çok daha hızlı , en hızlısı hala bir satır içi döngüdür.
Benjamin Gruenbaum

1
@BenjaminGruenbaum - bunu gösteren bazı sonuçlara bağlantı gönderebilir misiniz? Bildiğim kadarıyla kullanarak, bu yorumun sonunda bağlantılı jsperf toplanan sonuçlardan görüldüğü gibi .forEachdaha hızlı olduğu .push.applyKrom / Chromium'da (V25 beri tüm sürümlerinde). V8'i tek başına test edemedim, ancak varsa lütfen sonuçlarınızı bağlayın. Bkz. Jsperf: jsperf.com/array-extending-push-vs-concat/5
jcdude

1
@jcdude jsperf dosyanız geçersiz. sizin dizideki tüm unsurları olarak undefined, bu yüzden .forEachen hızlı o hale onları atlar. .spliceaslında en hızlı mı ?! jsperf.com/array-extending-push-vs-concat/18
EaterOfCode

152

Bugünlerde en şık olanı hissediyorum:

arr1.push(...arr2);

Yayılma operatörü MDN makale ES2015 (ES6) bu güzel şekerli yol bahseder:

Daha iyi bir itme

Örnek: push genellikle bir diziyi mevcut bir dizinin sonuna itmek için kullanılır. ES5'te bu genellikle şu şekilde yapılır:

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

ES6'da yayılma ile birlikte:

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

Not Do arr2büyük olması çağrı yığını jcdude cevabı gereğince, taşmaları, çünkü (000 100 hakkında kalemleri altında tutmak) olamaz.


1
Sadece yaptığım bir hatadan bir uyarı. ES6 sürümü çok yakın arr1.push(arr2). Bu arr1 = []; arr2=['a', 'b', 'd']; arr1.push(arr2), sonuçta arr1 == [['a','b','d']]iki dizinin birleştirilmesinden ziyade bir dizi dizisi olması gibi bir sorun olabilir . Yapması kolay bir hatadır. Bu nedenle aşağıdaki ikinci cevabınızı tercih ederim. stackoverflow.com/a/31521404/4808079
Seph Reed

Kullanırken kolaylık .concatvardır, ikinci argüman bir dizi olmamalıdır. Bu, spread operatörü ile birleştirilerek elde edilebilir concat, örneğinarr1.push(...[].concat(arrayOrSingleItem))
Steven Pribilinskiy

Bu, hedef dizinin boş olmadığı durumlar için modern, zarif Javascript'tir.
timbo

Bu yeterince hızlı mı?
Roel

@RoelDelosReyes: Evet.
odinho - Velmont

46

apply()JavaScript'i neden kullandığımızı anlamanıza yardımcı olacak birkaç kelime :

apply()Yöntem, belirli bir bir işlevini çağırır thisdeğeri ve bağımsız değişkenler, bir dizi olarak verdi.

Push , diziye eklenecek öğelerin bir listesini bekler . Ancak apply()yöntem, işlev çağrısı için beklenen bağımsız değişkenleri dizi olarak alır. Bu push, bir dizinin elemanlarını yerleşik push()yöntemle kolayca başka bir diziye dönüştürmemizi sağlar .

Şu dizilere sahip olduğunuzu düşünün:

var a = [1, 2, 3, 4];
var b = [5, 6, 7];

ve basitçe bunu yapın:

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

Sonuç:

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

Aynı şey ES6'da forma operatörü (" ...") şu şekilde yapılabilir :

a.push(...b); //a = [1, 2, 3, 4, 5, 6, 7]; 

Daha kısa ve daha iyi, ancak şu anda tüm tarayıcılarda tam olarak desteklenmiyor.

Eğer istediğiniz Ayrıca eğer hareket diziden şeyi bile aboşaltma, bsüreç içinde, bunu yapabilirsiniz:

while(b.length) {
  a.push(b.shift());
} 

ve sonuç aşağıdaki gibi olacaktır:

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

1
ya da while (b.length) { a.push(b.shift()); }değil mi?
LarsW

37

JQuery kullanmak istiyorsanız $ .merge () var

Misal:

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

Sonuç: a = [1, 2, 3, 4, 5]


1
Uygulamasını (kaynak kodunda) nasıl bulabilirim jQuery.merge()?
ma11hew28

Bana 10 dakika sürdü - jQuery açık kaynak ve kaynak Github'da yayınlandı. "Birleştirme" için bir arama yaptım ve core.js dosyasında birleştirme işlevinin tanımı gibi görünen bir şey buldum, bkz: github.com/jquery/jquery/blob/master/src/core.js#L331
amn

19

a.push.apply(a, b)Yukarıda açıklanan yöntemi seviyorum ve isterseniz her zaman böyle bir kütüphane işlevi oluşturabilirsiniz:

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

ve böyle kullan

a = [1,2]
b = [3,4]

a.append(b)

6
Push.apply yöntemi, "dizi" bağımsız değişkeniniz büyük bir diziyse (ör. Chrome'da> ~ 150000 giriş) bir yığın taşmasına neden olabileceğinden (ve bu nedenle başarısız olabileceğinden) kullanılmamalıdır. "Array.forEach" kullanmalısınız - cevabımı görün: stackoverflow.com/a/17368101/1280629
jcdude

12

Bunu kullanarak yapmak mümkündür splice():

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

Ancak daha çirkin olmasına rağmen push.apply, en azından Firefox 3.0'da daha hızlı değil.


9
Aynı şeyi buldum, splice, yaklaşık 10.000 eleman dizisine kadar her öğeyi itmek için performans geliştirmeleri sağlamaz jsperf.com/splice-vs-push
Drew

1
+1 Bunu ve performans karşılaştırmasını eklediğiniz için teşekkürler, bu yöntemi test etme çabamı kurtardım.
jjrv

1
Splice.apply veya push.apply öğelerinin kullanılması, dizi b büyükse yığın taşması nedeniyle başarısız olabilir. : - Onlar da yavaş bir veya "forEach" döngü "için" kullanmaktan daha vardır bu jsPerf bkz jsperf.com/array-extending-push-vs-concat/5 ve cevabım stackoverflow.com/questions/1374126/...
jcdude

4

Bu çözüm benim için çalışıyor (ECMAScript 6'nın spread operatörünü kullanarak):

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


1

Aşağıda olduğu gibi uzatmak için bir çoklu dolgu oluşturabilirsiniz. Diziye eklenir; Yerinde ve geri dön, böylece diğer yöntemleri zincirleyebilirsiniz.

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


0

Cevapları birleştirmek ...

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

9
Hayır, buna gerek yok. ForEach kullanarak bir for döngüsü push.apply kullanmaktan daha hızlıdır ve dizinin uzunluğu ne kadar olursa olsun çalışır. Gözden geçirilmiş cevabıma bir göz atın: stackoverflow.com/a/17368101/1280629 Her durumda, 150000'ün tüm tarayıcılar için doğru sayı olduğunu nasıl bilebilirsiniz? Bu bir şekerleme.
jcdude

lol. cevabım tanınmıyor, ancak o zaman bulunan başkalarının bir kombinasyonu gibi görünüyor - yani bir özet. endişelenmeyin
zCoder

0

İkiden fazla diziyi birleştirmek için başka bir çözüm

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

Ardından şu şekilde arayın ve yazdırın:

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

Çıktı: Array [1, 2, 3, 4, 5, 6, 7]


-1

Cevap çok basit.

>>> 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, JavaScript dizesi birleştirmesine çok benzer şekilde davranır. İşlevi çağırdığınız dizinin sonundaki concat işlevine koyduğunuz parametrenin bir birleşimini döndürür. Temel nokta, döndürülen değeri bir değişkene atamanız veya kaybolmasıdır. Yani mesela

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

2
MDN için dokümanlardaArray.prototype.concat() açıkça belirtildiği gibi bu yeni bir Dizi döndürecektir , OP'nin açıkça sorduğu gibi mevcut diziye eklenmeyecektir.
Solgunluk

-2

150.000'den fazla kayıt Array.extendyerine kullanın Array.push.

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

-6

Çok basit, forma operatörlerine güvenmez veya bu bir sorun varsa geçerli değildir.

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

Bu konuda bazı performans testleri yaptıktan sonra, son derece yavaş, ancak yeni bir dizi oluşturmama sorusunu cevaplıyor. Concat önemli ölçüde daha hızlı, hatta jQuery bile $.merge().

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


4
Dönüş değerini kullanmamanız .forEachyerine .mapkullanmalısınız. Kodunuzun amacını daha iyi iletir.
david

@david - her birine kendi. Ben sinax'ı tercih ederim .mapama siz de yapabilirsiniz b.forEach(function(x) { a.push(x)} ). Aslında bunu jsperf testine ekledim ve haritadan daha hızlı bir saç. Hala süper yavaş.
elPastor

3
Bu bir tercih meselesi değil. Bu 'işlevsel' yöntemlerin her birinin kendileriyle ilişkili belirli bir anlamı vardır. .mapBurada aramak çok garip görünüyor. Aramak gibi bir şey olurdu b.filter(x => a.push(x)). Çalışıyor, ancak olmadığı zaman bir şey olduğunu ima ederek okuyucuyu karıştırıyor.
david

2
@ elPastor zaman ayırmaya değer olmalı, çünkü benim gibi başka bir adam oturup kodunda başka neler olduğunu merak ederek boynunun enseğini çizecekti. Çoğu olağan durumda, performansın değil önemli olan niyetin açıklığıdır. Lütfen kodunuzu okuyanların zamanına saygı gösterin, çünkü sadece bir olduğunuzda çok sayıda olabilir. Teşekkürler.
Dmytro Y.

1
@elPastor Tam olarak ne yaptığını biliyorum .map()ve sorun bu. Bu bilgi beni ortaya çıkan diziye ihtiyacınız olduğunu düşündürür, aksi takdirde .forEach()okuyucuyu geri arama işlevinin yan etkileri konusunda temkinli hale getirmek için kullanmak iyi bir şey olacaktır . Çözümünüzün açık konuşmak gerekirse, ek bir doğal sayılar dizisi (sonuçları push) oluştururken, soru şunu belirtmektedir: "yeni bir dizi oluşturmadan".
Dmytro Y.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.