Javascript - başka bir dizinin içine bir dizi ekle


91

Başka bir dizinin içine bir dizi eklemenin daha verimli yolu nedir?

a1 = [1,2,3,4,5];
a2 = [21,22];

newArray - a1.insertAt(2,a2) -> [1,2, 21,22, 3,4,5];

A2 dizisi büyükse, splice kullanarak a2 yinelemesi performans açısından biraz garip görünüyor.

Teşekkürler.



2
bu bir öğe değil, bir dizi, bu yüzden ekleme çalışmıyor
ic3

Yanıtlar:


182

spliceBazı applyhilelerle birlikte kullanabilirsiniz :

a1 = [1,2,3,4,5];
a2 = [21,22];

a1.splice.apply(a1, [2, 0].concat(a2));

console.log(a1); // [1, 2, 21, 22, 3, 4, 5];

ES2015 + 'da, bunu biraz daha güzel hale getirmek için bunun yerine yayma operatörünü kullanabilirsiniz

a1.splice(2, 0, ...a2);

10
@icCube: Bu yöntemi Patrick'in cevabındaki yöntemle karşılaştırarak bir kıyaslama yaptım. Örnekte olduğu gibi a1 ve a2 ile başlar ve a2 değişkenini a1'e 10.000 kere enjekte eder (böylece sonunda 20.005 elemanlı bir diziniz olur). Bu yöntem: 81ms aldı , dilim + concat yöntemi 156ms aldı (Chrome 13'te test edildi) . Elbette bu, çoğu durumda okunaklılığın hızdan daha önemli olacağı şeklindeki standart uyarı ile birlikte gelir.
nickf

3
@nick: Chrome'da, diziler 130000'den fazla öğe tutarsa ​​bir yığın aşımı applyistisnası atar. jsfiddle.net/DcHCY jsPerf'de de oluyor FF ve IE'ye inanıyorum, eşiğin 500k civarında olduğuna inanıyorum
Hayır

splice'ı doğrudan çağırmak ve argümanları iletmek yerine neden application'ı kullanıyorsunuz?
Peter P.

@ FrançoisWahl, bu insanların cevaplarında genellikle görmezden geldikleri ciddi bir konudur. Örneğinizi aldım ve 1M öğelerinin kuzeyinde çalışmasını sağladım: stackoverflow.com/a/41466395/1038326
Gabriel Kohen

Bunu yeni denedim (Haziran 2017) ve hem küçük dizi boyutları hem de büyük dizi boyutları (Chrome, FF ve Edge'de) için en hızlı yol target.slice(0, insertIndex).concat(insertArr, target.slice(insertIndex)); jsperf.com/inserting-an-array-within-an-array
Alex gibi görünüyor Dima


14

İlk başta yanılmıştı. Bunun concat()yerine kullanılmalıydı .

var a1 = [1,2,3,4,5],
    a2 = [21,22],
    startIndex = 0,
    insertionIndex = 2,
    result;    

result = a1.slice(startIndex, insertionIndex).concat(a2).concat(a1.slice(insertionIndex));

Örnek: http://jsfiddle.net/f3cae/1/

Bu ifade, öğesinin ilk iki öğesini döndürmek için slice(0, 2)[dokümanlar] ' ı kullanır a1(burada 0başlangıç ​​dizini ve değiştirilmemesine 2rağmen deleteCount öğesidir a1).

Ara sonuç :[1,2]

Daha sonra, concat(a2)sayfanın sonuna eklemek a2için [dokümanlar] kullanır [1,2].

Orta sonuç : [1,2,21,22].

Ardından, bu ifadenin sonundaki a1.slice(2)bir sonda çağrılır .concat(), bu da anlamına gelir [1,2,21,22].concat(a1.slice(2)).

slice(2)Pozitif bir tamsayı argümanına sahip bir çağrı , 2. öğeden sonraki tüm öğeleri doğal sayılarla sayarak döndürecektir (olduğu gibi, beş öğe vardır, bu nedenle [3,4,5]döndürülecektir a1). Bunu söylemenin bir başka yolu da, tekil tamsayı indeksi argümanının a1.slice(), dizideki hangi konumda elemanların döndürülmeye başlanacağını söylemesidir (dizin 2, üçüncü elemandır).

Ara sonuç :[1,2,21,22].concat([3,4,5])

Son olarak ikincisi , sonuna .concat()eklenir .[3,4,5][1,2,21,22]

Sonuç :[1,2,21,22,3,4,5]

Değiştirmek cazip gelebilir Array.prototype, ancak prototip kalıtım kullanarak Array nesnesini basitçe genişletebilir ve söz konusu yeni nesneyi projelerinize enjekte edebilirsiniz.

Ancak, sınırda yaşayanlar için ...

Örnek: http://jsfiddle.net/f3cae/2/

Array.prototype.injectArray = function( idx, arr ) {
    return this.slice( 0, idx ).concat( arr ).concat( this.slice( idx ) );
};

var a1 = [1,2,3,4,5];
var a2 = [21,22];

var result = a1.injectArray( 2, a2 );

7 -> [1,2, [21,22], 3,4,5] değil 6 öğeli a1 alıyorum
ic3

İşler ! çok teşekkürler, birkaç gün bekleyelim ama bu en iyi cevap ... bunun için bir js işlevinin olmaması garip.
ic3

Bunu daha çok beğendim
Kimchi Man

Özetle, JS motoru, ifade tamamlanmadan önce dört dizi döndürmelidir. Çözümün mantığını beğendim, ancak alan konuları açısından optimal olmayabilir. Bununla birlikte, şık ve çapraz tarayıcı uyumlu olduğunu kabul ediyorum. Koleksiyonlar üzerinde çalıştığı için yayma işlecini kullanmanın bir maliyeti vardır, ancak bunu uzun vadede dört dizi döndürmeyi devralabilirim.
Anthony Rutledge,

4

Yayılmış operatörün bir ekspresyon ya (dizi hazır için) birden fazla elemanları (fonksiyon aramalar için) birden fazla bağımsız değişkenler beklenen yerlere genişletilmiş sağlar.

a2 = [21,22];
a1 = [1,2,...a2,3,4,5];//...a2 is use of spread operator
console.log(a1);


3

Bunu splice()yinelemeden ve yinelemeden yapmanın bir yolunu bulmak istedim : http://jsfiddle.net/jfriend00/W9n27/ .

a1 = [1,2,3,4,5];
a2 = [21,22];

a2.unshift(2, 0);          // put first two params to splice onto front of array
a1.splice.apply(a1, a2);   // pass array as arguments parameter to splice
console.log(a1);           // [1, 2, 21, 22, 3, 4, 5];

Genel amaçlı işlev biçiminde:

function arrayInsertAt(destArray, pos, arrayToInsert) {
    var args = [];
    args.push(pos);                           // where to insert
    args.push(0);                             // nothing to remove
    args = args.concat(arrayToInsert);        // add on array to insert
    destArray.splice.apply(destArray, args);  // splice it in
}

Bu a2, muhtemelen oldukça istenmeyen bir durum olan diziyi de değiştirir . Ayrıca, Array.prototypedilim işlevine sahip olduğunuzda oraya a1veya üzerine gitmenize gerek yoktur a2.
nickf

A2'yi modifiye ettiğinin farkındaydım. OP bunun bir sorun olup olmadığına karar verebilir. Yöntemi almak için prototipe gitmenin yanlış bir yanı var mı? Sadece metodu istediğim ve metoda obje eklendiği için bana daha uygun geldi apply().
jfriend00

Hayır, bu tam olarak aynı işlev, ancak biri daha kısa: Array.prototype.splicevs a1.splice:)
nickf

Genel amaçlı dizi ekleme işlevi eklendi.
jfriend00

.concatyeni bir dizi döndürür, mevcut olanı değiştirmez splice. Olmalıargs = args.concat(arrayToInsert);
nickf

3

Burada bu soruya gerçekten yaratıcı cevaplar var. İşte dizilerle yeni başlayanlar için basit bir çözüm. İstenirse ECMAScript 3 uyumlu tarayıcılara kadar çalışabilir hale getirilebilir.

Başlamadan önce ekleme hakkında bir şeyler öğrenin.

Mozilla Geliştirici Ağı: Array.prototype.splice ()

İlk olarak, iki önemli formunu anlayın .splice().

let a1 = [1,2,3,4],
    a2 = [1,2];

Yöntem 1) İstenen bir dizinden başlayarak x (deleteCount) öğelerini kaldırın.

let startIndex = 0, 
    deleteCount = 2;

a1.splice(startIndex, deleteCount); // returns [1,2], a1 would be [3,4]

Yöntem 2) İstenen bir başlangıç ​​dizininden sonra dizinin sonuna kadar öğeleri kaldırın.

a1.splice(2); // returns [3,4], a1 would be [1,2]

Kullanarak .splice(), a1yukarıdaki iki formdan birini kullanarak baş ve kuyruk dizilerini ayırmak bir amaç olabilir .

Yöntem # 1 kullanıldığında, dönüş değeri başlık ve a1kuyruk olur.

let head = a1.splice(startIndex, deleteCount); // returns [1,2], a1 would be [3,4]

Şimdi, bir çırpıda düştü, baş, gövde ( a2) ve kuyruğu birleştirin

[].concat(head, a2, a1);

Dolayısıyla, bu çözüm şimdiye kadar sunulan diğer çözümlerden çok gerçek dünyaya benziyor. Legolarla yapacağın bu değil mi? ;-) Burada yöntem # 2 kullanılarak yapılan bir işlev var.

/**
*@param target Array The array to be split up into a head and tail.
*@param body Array The array to be inserted between the head and tail.
*@param startIndex Integer Where to split the target array.
*/
function insertArray(target, body, startIndex)
{
    let tail = target.splice(startIndex); // target is now [1,2] and the head
    return [].concat(target, body, tail);
}

let newArray = insertArray([1, 2, 3, 4], ["a", "b"], 2); // [1, 2, "a", "b", 3, 4]

Daha kısa:

/**
*@param target Array The array to be split up into a head and tail.
*@param body Array The array to be inserted between the head and tail.
*@param startIndex Integer Where to split the target array.
*/
function insertArray(target, body, startIndex)
{
    return [].concat(target, body, target.splice(startIndex));
}

Daha güvenli:

/**
*@param target Array The array to be split up into a head and tail.
*@param body Array The array to be inserted between the head and tail.
*@param startIndex Integer Where to split the target array.
*@throws Error The value for startIndex must fall between the first and last index, exclusive.
*/
function insertArray(target, body, startIndex)
{
    const ARRAY_START = 0,
          ARRAY_END = target.length - 1,
          ARRAY_NEG_END = -1,
          START_INDEX_MAGNITUDE = Math.abs(startIndex);

    if (startIndex === ARRAY_START) {
        throw new Error("The value for startIndex cannot be zero (0).");
    }

    if (startIndex === ARRAY_END || startIndex === ARRAY_NEG_END) {
        throw new Error("The startIndex cannot be equal to the last index in target, or -1.");
    }

    if (START_INDEX_MAGNITUDE >= ARRAY_END) {
        throw new Error("The absolute value of startIndex must be less than the last index.");
    }

    return [].concat(target, body, target.splice(startIndex));
}

Bu çözümün avantajları şunları içerir:

1) Çözüme basit bir öncül hakimdir - boş bir diziyi doldurun.

2) Baş, vücut ve kuyruk terminolojisi doğaldır.

3) Çift çağrı yok .slice(). Hiç dilimleme yok.

4) Hayır .apply(). Son derece gereksiz.

5) Yöntem zincirlemesinden kaçınılır.

6) ECMAScript 3 ve 5'te veya varyerine kullanarak basitçe çalışır .letconst

** 7) Sunulan diğer çözümlerden farklı olarak vücuda tokatlanacak bir baş ve kuyruk olmasını sağlar. Sınırlardan önce veya sonra bir dizi ekliyorsanız, en azından kullanıyor olmalısınız .concat()!!!!

Not: Yayılma opearatörünün kullanılması ..., tüm bunların gerçekleştirilmesini çok daha kolay hale getirir.


0
var a1 = [1,2,3,4,5];
var a2 = [21,22];

function injectAt(d, a1, a2) {
    for(var i=a1.length-1; i>=d; i--) {
        a1[i + a2.length] = a1[i];
    }
    for(var i=0; i<a2.length; i++) {
        a1[i+d] = a2[i];
    }
}

injectAt(2, a1, a2);

alert(a1);

0

İşte özel numara içermeyen versiyonum:

function insert_array(original_array, new_values, insert_index) {
    for (var i=0; i<new_values.length; i++) {
        original_array.splice((insert_index + i), 0, new_values[i]);
    }
    return original_array;
}

Bu durumda, her yineleme için insert_index'i artırmak yerine, yeni_değerler dizisini .reverse () yapmak daha basit olabilir. Elbette, start_index sıfır veya start_index - 1 olduğunda, bu çözümün verimliliği, herhangi bir açık döngü olmadan .concat () veya unshift () veya push () `ile karşılaştırıldığında zayıftır .apply().
Anthony Rutledge

0

Yeni bir dizi oluşturmadan bir diziye başka bir dizi eklemek istiyorsanız, en kolay yol ya pushda unshiftile kullanmaktır.apply

Örneğin:

a1 = [1,2,3,4,5];
a2 = [21,22];

// Insert a1 at beginning of a2
a2.unshift.apply(a2,a1);
// Insert a1 at end of a2
a2.push.apply(a2,a1);

Bu işe yarar çünkü hem pushve hem de unshiftdeğişken sayıda argüman alır. Bir bonus, diziyi hangi uçtan bağlayacağınızı kolayca seçebilirsiniz!


Sadece yaparak a1.concat(a2)ya da a2.concat(a1)önerdiğinden daha kolay bir şey tasarladım. Bununla birlikte, sorun, yalnızca başlangıca veya sona bir dizi eklemek değil, dizi sınırları arasına bir dizi eklemektir. ;-)
Anthony Rutledge

0

Başka bir iş parçacığında belirtildiği gibi, yukarıdaki cevaplar çok büyük dizilerde (200K eleman) çalışmayacaktır. Ekleme ve manuel itmeyi içeren alternatif yanıtı burada görün: https://stackoverflow.com/a/41465578/1038326

Array.prototype.spliceArray = function(index, insertedArray) {
    var postArray = this.splice(index);
    inPlacePush(this, insertedArray);
    inPlacePush(this, postArray);

    function inPlacePush(targetArray, pushedArray) {
  // Not using forEach for browser compatability
        var pushedArrayLength = pushedArray.length;
        for (var index = 0; index < pushedArrayLength; index++) {
           targetArray.push(pushedArray[index]);
       }
    }
}
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.