Dizi öğelerini başka bir diziye kopyalama


916

dataArrayYeni bir dizi içine itmek istiyorum bir JavaScript dizisi var newArray. İstemediğim hariç newArray[0]olmak dataArray. Tüm öğeleri yeni diziye itmek istiyorum:

var newArray = [];

newArray.pushValues(dataArray1);
newArray.pushValues(dataArray2);
// ...

veya daha iyisi:

var newArray = new Array (
   dataArray1.values(),
   dataArray2.values(),
   // ... where values() (or something equivalent) would push the individual values into the array, rather than the array itself
);

Şimdi yeni dizi tek tek veri dizilerinin tüm değerlerini içeriyor. Öğeleri tek tek ekleyerek, pushValuesher bir birey üzerinde tekrarlamak zorunda kalmamam gibi mevcut stenografi var mı dataArray?




Yanıtlar:


1248

Concat işlevini şu şekilde kullanın :

var arrayA = [1, 2];
var arrayB = [3, 4];
var newArray = arrayA.concat(arrayB);

Değeri newArrayolacaktır [1, 2, 3, 4]( arrayAve arrayBdeğişmeden kalır; concatsonuç için yeni bir dizi oluşturur ve döndürür).


5
Daha iyi bir referans
şöyledir

16
Performans uygulamasının çok güzel olduğuna katılıyorum. AMA için concat tam değil o kadar amaçla concat diziler için? Bu yüzden standart olmalı . Yoksa concat ile ilgili daha iyi şeyler var mı? Ve sadece tarayıcının JS motorunun kötü uygulanması veya nerede kullandığınız her yerde yavaş olabilir mi? Bir gün düzeltilebilir. Keskin hız optimizasyonlarına göre kod sürdürülebilirliğini seçerdim. Hmm ....
Bitterblue

3
Ayrıca sadece durumu karşılaştırdım: concat vs. push.apply. Google Chrome: hızlı (concat = kazanan) ,: Operahızlı (concat = kazanan) ,: IEdaha yavaş (concat = kazanan) Firefox,: yavaş (push.apply = kazanan, ancak Chrome'un concat'tan 10 kat daha yavaş) ... kötü JS motor uygulamasından bahsedin .
Bitterblue

15
Nasıl olduğunu birleştirerek iki diziyi nasıl için kabul edilen cevap itmek başka içine bir tane ?! Bunlar iki farklı operasyon.
kaqqao

@kaqqao çünkü pushbir değer dizisini düzleştirmez. concatsorunun ne gerektirdiğini başarır.
WiseGuyEh

644

Dizileriniz çok büyük değilse (aşağıdaki uyarıya bakın), push()değer eklemek istediğiniz dizinin yöntemini kullanabilirsiniz . işlev parametrelerinin bir listesi olarak iletilecek değer dizisini iletmek için yöntemini push()kullanabilmeniz için birden çok parametre alabilir apply(). Bunun avantajı, concat()yeni bir dizi oluşturmak yerine diziye öğelerin eklenmesi üzerine kullanılmasıdır .

Bununla birlikte, büyük diziler için (100.000 veya daha fazla üyeden oluşan) bu hile başarısız olabilir . Bu tür diziler için, bir döngü kullanmak daha iyi bir yaklaşımdır. Ayrıntılar için https://stackoverflow.com/a/17368101/96100 adresine bakın.

var newArray = [];
newArray.push.apply(newArray, dataArray1);
newArray.push.apply(newArray, dataArray2);

Bunu bir işlevde genelleştirmek isteyebilirsiniz:

function pushArray(arr, arr2) {
    arr.push.apply(arr, arr2);
}

... veya Arrayprototipine ekleyin :

Array.prototype.pushArray = function(arr) {
    this.push.apply(this, arr);
};

var newArray = [];
newArray.pushArray(dataArray1);
newArray.pushArray(dataArray2);

... ya da orijinal taklit push()gerçeğini kullanarak birden çok parametre izin vererek yöntemi concat()gibi push(), birden çok parametre verir:

Array.prototype.pushArray = function() {
    this.push.apply(this, this.concat.apply([], arguments));
};

var newArray = [];
newArray.pushArray(dataArray1, dataArray2);

IE <= 8 dahil olmak üzere büyük diziler ve tüm büyük tarayıcılar için uygun olan son örneğin döngü tabanlı bir sürümü:

Array.prototype.pushArray = function() {
    var toPush = this.concat.apply([], arguments);
    for (var i = 0, len = toPush.length; i < len; ++i) {
        this.push(toPush[i]);
    }
};

9
not: newArray.push.apply(newArray, dataArray1);ile aynı verirArray.prototype.push.applay(newArray,dataArra1);

Bu eski tarayıcılarla uyumlu mu?
Damien Ó Ceallaigh


1
@ DamienÓCeallaigh: Evet. Bu, IE 6'ya ve daha öncesine giden tarayıcılarla uyumludur.
Tim Down

Bu kötü programlama uygulamalarının özüdür. Array.prototype.concatkötü değil, sadece yanlış anlaşılır ve bazen yanlış kullanılır. Bu koşullar altında, sadece yanlış anlaşılır, ancak yanlış kullanılmaz.
Jack Giffin

444

Bir tane daha "geleceğe hazır" cevap ekleyeceğim

ECMAScript 6'da, Spread sözdizimini kullanabilirsiniz :

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

console.log(arr1)

Spread sözdizimi henüz tüm büyük tarayıcılara dahil edilmemiştir. Geçerli uyumluluk için bu (sürekli güncellenen) uyumluluk tablosuna bakın .

Ancak, Babel.js ile forma sözdizimini kullanabilirsiniz .

Düzenle:

Performansla ilgili daha fazla yorum için aşağıdaki Jack Giffin yanıtına bakın. Concat hala yayılan operatörden daha iyi ve daha hızlı görünüyor.


7
TypeScript kullanıyorsanız forma işlecini de kullanabilirsiniz. ES5'i hedeflerseniz, derlenir newArray.apply(newArray, dataArray1).
AJ Richardson

5
Not: üçüncü bir dizide sonuca ihtiyacınız varsa (bu nedenle ilk sorunun gerektirdiği gibi arr1'i değiştirmiyorsanız), newArray = [... arr1, ... arr2]
dim

Bunu bilmiyordum. Teşekkürler. Bir düzenleme yorumu ekledim.
Karel Bílek

Concat'ın işleyişine benzer şekilde, kalıcı olma bonusu ile (orijinal diziyi mutasyona uğratmak).
Rob

@robertmylne Forma işleci orijinal diziyi değiştirmez, bunun yerine desparsed tüm dizilerin yeni bir kopyasını oluşturur.
Jack Giffin

145

MDN'den zarif bir yol buldum

var vegetables = ['parsnip', 'potato'];
var moreVegs = ['celery', 'beetroot'];

// Merge the second array into the first one
// Equivalent to vegetables.push('celery', 'beetroot');
Array.prototype.push.apply(vegetables, moreVegs);

console.log(vegetables); // ['parsnip', 'potato', 'celery', 'beetroot']

Veya spread operatorES6 özelliğini kullanabilirsiniz :

let fruits = [ 'apple', 'banana'];
const moreFruits = [ 'orange', 'plum' ];

fruits.push(...moreFruits); // ["apple", "banana", "orange", "plum"]

1
Sorunun istediği bu değildi. Soru, eski bir diziyi değiştirmek yerine her seferinde yeni bir dizi oluşturmanın bir yolunu sormaktır.
Jack Giffin

13
var a=new Array('a','b','c');
var b=new Array('d','e','f');
var d=new Array('x','y','z');
var c=a.concat(b,d)

Bu sorununuzu çözüyor mu?


13

Aşağıdakiler benim için en basit gibi görünüyor:

var newArray = dataArray1.slice();
newArray.push.apply(newArray, dataArray2);

"Push" değişken sayıda bağımsız değişken aldığından , başka bir dizinin tüm öğelerini itmek applyiçin pushişlevin yöntemini kullanabilirsiniz . İlk argümanını (burada "newArray") "this" ve dizinin elemanlarını kalan argümanlar olarak kullanarak itme çağrısı oluşturur.

sliceİlk açıklamada ilk dizinin bir kopyasını alır, bu yüzden bunu değiştirmez.

Güncelle Dilim kullanılabilir bir javascript sürümü kullanıyorsanız, pushifadeyi basitleştirebilirsiniz :

newArray.push(...dataArray2)

1
Burada ayrıca MDN'de "İki diziyi birleştirmek" için bir örnek olarak bahsedilmektedir
Wilt

12

Aşağıdaki işlevin dizilerin uzunluğu ile ilgili bir sorunu yoktur ve önerilen tüm çözümlerden daha iyi performans gösterir:

function pushArray(list, other) {
    var len = other.length;
    var start = list.length;
    list.length = start + len;
    for (var i = 0; i < len; i++ , start++) {
        list[start] = other[i];
    }
}

ne yazık ki, jspref gönderimlerimi kabul etmeyi reddediyor, bu yüzden benchmark.js kullanan sonuçlar

        Name            |   ops/sec   |  ± %  | runs sampled
for loop and push       |      177506 |  0.92 | 63
Push Apply              |      234280 |  0.77 | 66
spread operator         |      259725 |  0.40 | 67
set length and for loop |      284223 |  0.41 | 66

nerede

döngü ve itme için:

    for (var i = 0, l = source.length; i < l; i++) {
        target.push(source[i]);
    }

Push Uygula:

target.push.apply(target, source);

yayma operatörü:

    target.push(...source);

ve son olarak 'ayarlanan uzunluk ve döngü için' yukarıdaki işlevdir


Bu soru, her seferinde yeni bir dizi oluşturmanın bir yolunu arıyor, mevcut bir diziyi değiştirmiyor.
Jack Giffin

10

𝗥𝗲𝘀𝗲𝗮𝗿𝗰𝗵 𝗔𝗻𝗱 𝗥𝗲𝘀𝘂𝗹𝘁𝘀

Gerçekler için, jsperf'de bir performans testi ve konsoldaki bazı şeylerin kontrol edilmesi gerçekleştirilir. Araştırma için irt.org web sitesi kullanılmıştır. Aşağıda, bir araya getirilen tüm bu kaynakların bir koleksiyonu ve altta bir örnek işlev bulunmaktadır.

╔═══════════════╦══════╦═════════════════╦════════ ═══════╦═════════╦══════════╗
║ Yöntemi
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ mOps / Saniye ║179 ║104 ║ 76 ║ 81 ║28 ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Seyrek diziler ║Evet! ║Sadece dilimlenmiş ║ hayır ║ Belki 2   ║no ║
║ seyrek tutulmuş ║ ║array (1. arg) ║ ║ ║ ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Destek ║MSIE 4║MSIE 5.5 ║ MSIE 5.5 ║ MSIE 4 ║ Kenar 12 ║
║ ( kaynak ) ║NNav 4║NNav 4.06 ║ NNav 4.06 ║ NNav 3 ║ MSIE  NNav ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Dizi benzeri eylemler║ hayır nSadece itti ║ EVET! ║ EVET! HaveEğer varsa ║
Bir dizi ║ ║array (2 arg) ║ ║ ║iterator ║like 1   ║
╚═══════════════╩══════╩═════════════════╩════════ ═══════╩═════════╩══════════╝
1 Dizi benzeri bir nesnenin Symbol.iterator özelliği yoksa ,
  yaymak bir istisna atar.
2 Koda bağlıdır. Aşağıdaki örnek kod "EVET" seyrekliği korur.
function mergeCopyTogether(inputOne, inputTwo){
   var oneLen = inputOne.length, twoLen = inputTwo.length;
   var newArr = [], newLen = newArr.length = oneLen + twoLen;
   for (var i=0, tmp=inputOne[0]; i !== oneLen; ++i) {
        tmp = inputOne[i];
        if (tmp !== undefined || inputOne.hasOwnProperty(i)) newArr[i] = tmp;
    }
    for (var two=0; i !== newLen; ++i, ++two) {
        tmp = inputTwo[two];
        if (tmp !== undefined || inputTwo.hasOwnProperty(two)) newArr[i] = tmp;
    }
    return newArr;
}

Yukarıda görüldüğü gibi, Concat'ın neredeyse her zaman hem performans hem de yedek dizilerin seyrekliğini koruyabilme yolu olduğunu iddia ediyorum. Daha sonra, dizi beğenileri (örneğin DOMNodeLists gibi document.body.children) için, for döngüsünü kullanmanızı tavsiye ederim çünkü hem en yüksek 2. performans hem de seyrek dizileri tutan tek diğer yöntemdir. Aşağıda, karışıklığı gidermek için seyrek diziler ve dizi beğenileriyle kastedilen şeyleri hızla ele alacağız.

𝗧𝗵𝗲 𝗙𝘂𝘁𝘂𝗿𝗲

İlk başta, bazı insanlar bunun bir fluke olduğunu düşünebilir ve tarayıcı satıcıları sonunda Array.prototype.push'u Array.prototype.concat'i yenecek kadar hızlı olacak şekilde optimize etmeye çalışacaklardır. YANLIŞ! Array.prototype.concat her zaman daha hızlı olacaktır (prensip olarak en azından) çünkü veriler üzerinde basit bir kopyala-yapıştır. Aşağıda, 32 bit dizi uygulamasının nasıl görünebileceğine dair basitleştirilmiş ikna-görsel bir diyagram bulunmaktadır (lütfen gerçek uygulamaların çok daha karmaşık olduğunu unutmayın)

Bayt ║ Burada veriler
═════╬═══════════
0x00 ║ int nonNumericPropertiesLength = 0x00000000
0x01 ║ age.
0x02 id age.
0x03 id age
0x00 ║ int uzunluğu = 0x00000001
0x01 ║ age.
0x02 id age.
0x03 id age
0x00 ║ int valueIndex = 0x00000000
0x01 ║ age.
0x02 id age.
0x03 id age
0x00 ║ int valueType = JS_PRIMITIVE_NUMBER
0x01 ║ age.
0x02 id age.
0x03 id age
0x00 ║ uintptr_t valuePointer = 0x38d9eb60 (veya bellekte olduğu her yerde)
0x01 ║ age.
0x02 id age.
0x03 id age

Yukarıda görüldüğü gibi, böyle bir şeyi kopyalamak için yapmanız gereken tek şey bayt için bayt kopyalamak kadar basittir. Array.prototype.push.apply ile, veriler üzerinde basit bir kopyala-yapıştırdan çok daha fazlasıdır. ".Apply" dizideki her dizini kontrol etmeli ve Array.prototype.push'a geçirmeden önce bir dizi argümana dönüştürmelidir. Daha sonra, Array.prototype.push her seferinde daha fazla bellek ayırmalıdır ve (bazı tarayıcı uygulamaları için) seyreklik için bazı konum arama verilerini bile yeniden hesaplayabilir.

Bunu düşünmenin alternatif bir yolu şudur. Birinci kaynak dizisi, birlikte zımbalanmış büyük bir kağıt yığınıdır. İkinci kaynak dizisi de bir başka büyük kağıt yığınıdır. Senin için daha hızlı olur mu

  1. Mağazaya gidin, her kaynak dizinin bir kopyası için gerekli kağıdı satın alın. Ardından, her kaynak dizisi yığınını bir fotokopi makinesinden geçirin ve elde edilen iki kopyayı birlikte zımbalayın.
  2. Mağazaya gidin, ilk kaynak dizinin tek bir kopyası için yeterli kağıdı satın alın. Ardından, kaynak diziyi boş kağıt noktalarının doldurulmasını sağlayarak yeni kağıda el ile kopyalayın. Ardından, mağazaya geri dönün, ikinci kaynak dizisi için yeterli kağıt satın alın. Ardından, ikinci kaynak diziyi gözden geçirin ve kopyada boş boşluk kalmamasını sağlarken kopyalayın. Ardından, kopyalanan tüm kağıtları birlikte zımbalayın.

Yukarıdaki benzetmede # 1 seçeneği Array.prototype.concat'i, # 2 ise Array.prototype.push.apply'yi temsil eder. Bunu, sadece katı diziler yerine seyrek diziler üzerindeki yöntemleri test ettiği için farklı bir JSperf ile test edelim. Bunu burada bulabilirsiniz .

Bu nedenle, bu özel kullanım durumu için performansın geleceğinin Array.prototype.push'ta değil, daha çok Array.prototype.concat'te yattığını söylüyorum.

𝗖𝗹𝗮𝗿𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻𝘀

𝗦𝗽𝗮𝗿𝗲 𝗔𝗿𝗿𝗮𝘆𝘀

Dizinin belirli üyeleri eksik olduğunda. Örneğin:

// This is just as an example. In actual code, 
// do not mix different types like this.
var mySparseArray = [];
mySparseArray[0] = "foo";
mySparseArray[10] = undefined;
mySparseArray[11] = {};
mySparseArray[12] =  10;
mySparseArray[17] = "bar";
console.log("Length:   ", mySparseArray.length);
console.log("0 in it:  ", 0 in mySparseArray);
console.log("arr[0]:   ", mySparseArray[0]);
console.log("10 in it: ", 10 in mySparseArray);
console.log("arr[10]   ", mySparseArray[10]);
console.log("20 in it: ", 20 in mySparseArray);
console.log("arr[20]:  ", mySparseArray[20]);

Alternatif olarak, javascript yedek dizileri kolayca başlatmanıza izin verir.

var mySparseArray = ["foo",,,,,,,,,,undefined,{},10,,,,,"bar"];

𝗔𝗿𝗿𝗮𝘆-𝗟𝗶𝗸𝗲𝘀

Dizi benzeri, en az lengthözelliği olan ancak new Arrayveya ile başlatılmamış bir nesnedir []. Örneğin, aşağıdaki nesneler dizi benzeri olarak sınıflandırılır.

{0: "foo", 1: "bar", uzunluk: 2}
document.body.children
yeni Uint8Array (3)
  • Bu, diziye benzer çünkü bir (n) (yazılan) dizi olmasına rağmen, onu bir diziye zorlamak yapıcıyı değiştirir.
(function () {return argümanları}) ()

Dizi beğenilerini dilim gibi dizilere zorlayan bir yöntem kullanarak neler olduğunu gözlemleyin.

  • NOT: Performans nedeniyle işlev bağımsız değişkenlerinde dilim çağırmak kötü bir uygulamadır.

Olmayan bir yöntemi kullanarak ne olur gözlemleyin değil concat gibi diziler içine dizisi-sever zorlamak.


9

JavaScript ES6 ile, diziyi esasen değerlere dönüştürecek bir yayılma operatörü olarak kullanabilirsiniz. Sonra böyle bir şey yapabilirsiniz:

const myArray = [1,2,3,4,5];
const moreData = [6,7,8,9,10];

const newArray = [
  ...myArray,
  ...moreData,
];

Sözdizimi kısa olsa da, bunun dahili olarak nasıl çalıştığını ve büyük dizilerdeki performans etkilerinin ne olduğunu bilmiyorum.


2
Babil'in onu nasıl dönüştürdüğüne bir bakarsanız, Array.push.applytekniği kullanmaktan daha yavaş olmaması gerektiğini göreceksiniz .
emil.c

1
@JackGiffin Sadece Ryan'ın dahili olarak nasıl çalıştığını bilmediğinden ve performans sonuçları nelerden bahsettiğinden bahsediyordum, aslında bu yaklaşımı önermiyordum. Her durumda, cevabınızda çok iyi bir iş çıkardınız, güzel araştırmalar, bu tür ayrıntıları bilmek her zaman iyidir.
emil.c

9

İşte ES6 yolu

var newArray = [];
let dataArray1 = [1,2,3,4]
let dataArray2 = [5,6,7,8]
newArray = [...dataArray1, ...dataArray2]
console.log(newArray)

Yukarıdaki yöntem, çoğu durumda ve concatdizilerde yüz binlerce öğeniz olduğu gibi lütfen dikkate alınmayan durumlar için iyidir .

    let dataArray1 = [1,2,3,4]
    let dataArray2 = [5,6,7,8]
    let newArray = dataArray1.concat(dataArray2);
    console.log(newArray)


8

Array.prototype.push.apply hakkında konuşan birkaç cevap var . İşte açık bir örnek:

var dataArray1 = [1, 2];
var dataArray2 = [3, 4, 5];
var newArray = [ ];
Array.prototype.push.apply(newArray, dataArray1); // newArray = [1, 2]
Array.prototype.push.apply(newArray, dataArray2); // newArray = [1, 2, 3, 4, 5]
console.log(JSON.stringify(newArray)); // Outputs: [1, 2, 3, 4, 5]

ES6 sözdiziminiz varsa:

var dataArray1 = [1, 2];
var dataArray2 = [3, 4, 5];
var newArray = [ ];
newArray.push(...dataArray1); // newArray = [1, 2]
newArray.push(...dataArray2); // newArray = [1, 2, 3, 4, 5]
console.log(JSON.stringify(newArray)); // Outputs: [1, 2, 3, 4, 5]


4

Orijinal diziyi değiştirmek isterseniz, aşağıdakileri yayabilir ve itebilirsiniz:

var source = [1, 2, 3];
var range = [5, 6, 7];
var length = source.push(...range);

console.log(source); // [ 1, 2, 3, 5, 6, 7 ]
console.log(length); // 6

sourceDiziye yalnızca aynı türden öğelerin girdiğinden emin olmak istiyorsanız (örneğin sayıları ve dizeleri karıştırmamak), TypeScript kullanın.

/**
 * Adds the items of the specified range array to the end of the source array.
 * Use this function to make sure only items of the same type go in the source array.
 */
function addRange<T>(source: T[], range: T[]) {
    source.push(...range);
}

2

İki dizi a ve b var. burada yapılan kod dizidir b dizisine bir değer itilir.

let a = [2, 4, 6, 8, 9, 15]

function transform(a) {
    let b = ['4', '16', '64']
    a.forEach(function(e) {
        b.push(e.toString());
    });
    return b;
}

transform(a)

[ '4', '16', '64', '2', '4', '6', '8', '9', '15' ]

1
Lütfen kodu sadece cevap olarak göndermeyin. Kodun ne yaptığını ve sorunu nasıl çözdüğünü açıklayın.
Patrick Hund

0

Bunu dene:

var arrayA = [1, 2];
var arrayB = [3, 4];
var newArray = arrayB.reduce((pre, cur) => [...pre, ...cur], arrayA);
console.log(newArray)

-2

push () işlevi yerine IE için concat işlevini kullanın. misal,

var a=a.concat(a,new Array('amin'));

1
ikisi de IE uyumlu
Jack Giffin

-3

Bu bir çalışma kodudur ve iyi çalışır:

var els = document.getElementsByTagName('input'), i;
var invnum = new Array();
var k = els.length;
for(i = 0; i < k; i++){invnum.push(new Array(els[i].id,els[i].value))}
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.