Diziye bir değer eklemenin en etkili yolu


268

Ben N(nerede N > 0) bir boyutu olan bir dizi var varsayalım, O (N + 1) adımları gerektirmeyen diziye prepending için daha verimli bir yolu var mı?

Kod olarak, aslında, şu anda ne yapıyorum

function prependArray(value, oldArray) {
  var newArray = new Array(value);

  for(var i = 0; i < oldArray.length; ++i) {
    newArray.push(oldArray[i]);
  }

  return newArray;
}

Bağlantılı listeleri ve işaretçileri sevdiğim kadar, yerel JS veri türlerini kullanarak bir şeyler yapmanın daha etkili bir yolu olması gerektiğini hissediyorum
samccone

@samccone: Evet yorumumu dikkate almayın üzgünüm, Java dediğini sanıyordum: P
GWW

3
Java, JavaScript, C veya Python, hangi dilin önemi yoktur: diziler ve bağlantılı listeler arasındaki karmaşıklık dengesi aynıdır. Bağlantılı Listeler muhtemelen JS'de oldukça kullanışsızdır, çünkü onlar için yerleşik bir sınıf yoktur (Java'nın aksine), ancak gerçekten istediğiniz şey O (1) ekleme süresiyse, bağlantılı bir liste istersiniz.
mgiuca

1
Klonlamak bir gereklilik mi?
Ryan Florence

1
Klonlamak için bir gereklilikse, orijinal diziyi değiştirecek ve bir kopya oluşturmayacağı için kaydırma, uygun değildir.
mgiuca

Yanıtlar:


492

Big-O açısından daha verimli olduğundan emin değilim ama unshiftyöntemi kullanmak kesinlikle daha özlü:

var a = [1, 2, 3, 4];
a.unshift(0);
a; // => [0, 1, 2, 3, 4]

[Düzenle]

Bu jsPerf kriter gösterileri unshiftbakılmaksızın muhtemelen farklı büyük-O performansı, terbiyeli hızlı tarayıcıların en az bir çift olduğu takdirde sen yerinde diziyi değiştirmeden ile ok. Orijinal diziyi gerçekten değiştiremezseniz, aşağıdaki snippet gibi bir şey yaparsınız, bu da çözümünüzden oldukça hızlı görünmüyor:

a.slice().unshift(0); // Use "slice" to avoid mutating "a".

[Düzenle 2]

Tamlık prependArray(...)için, Array unshift(...)yönteminden yararlanmak için OP örneği yerine aşağıdaki işlev kullanılabilir :

function prepend(value, array) {
  var newArray = array.slice();
  newArray.unshift(value);
  return newArray;
}

var x = [1, 2, 3];
var y = prepend(0, x);
y; // => [0, 1, 2, 3];
x; // => [1, 2, 3];

190
Kim prepend" unshift" demeye karar verdi ?
Scott Stafford

12
@ScottStafford unshiftböyle bir dizi işlemi için daha uygun geliyor (elemanları fiziksel olarak aşağı yukarı hareket ettiriyor ). öğelerin tam anlamıylaprepend önüne eklediğiniz bağlantılı listelere daha uygun olur .
CamilB

12
unshift, kaydırmanın tamamlayıcı işlevidir. Bunu "başa" olarak adlandırmak garip bir seçim olacaktır. Vurgulama, itme pop olduğu için kaydırmaktır.
Sir Robert

9
Bu çözümle ilgili yalnızca bir sorun var. Unshift (), bu yanıttaki gibi diziyi değil , uzunluğunu döndürmez mi? w3schools.com/jsref/jsref_unshift.asp
iDVB

4
pushve unshifther ikisini de içerir u, oysa popve shiftyok.
Qian Chen

70

ES6 ile artık orijinal öğelerin önüne yeni öğeleriniz eklenmiş yeni bir dizi oluşturmak için forma operatörünü kullanabilirsiniz .

// Prepend a single item.
const a = [1, 2, 3];
console.log([0, ...a]);

// Prepend an array.
const a = [2, 3];
const b = [0, 1];
console.log([...b, ...a]);

2018-08-17 Güncellemesi: Performans

Bu cevabı daha akılda kalıcı ve özlü olduğunu düşündüğüm alternatif bir sözdizimi sunmayı amaçladım. Bazı kriterlere göre ( bu diğer cevaba bakınız ), bu sözdiziminin önemli ölçüde daha yavaş olduğuna dikkat edilmelidir . Bu işlemlerin çoğunu bir döngü içinde yapmazsanız, bu muhtemelen önemli olmayacaktır.


4
Bu 2017 itibariyle oylanmalıdır. Kısa ve özlüdür. Formayı kullanarak, daha fazla modifikasyon için zincirleme yapmak için yararlı olabilecek yeni akışı geri verir. Aynı zamanda saftır (yani a ve b'nin etkilenmediği anlamına gelir)
Simon

Sen kıyasla alınan süre Abt söz etmediunshift
Shashank Vivek

Teşekkürler @ShashankVivek. Bu cevabı daha akılda kalıcı ve özlü olduğunu düşündüğüm alternatif bir sözdizimi sunmayı amaçladım. Güncelleyeceğim.
Frank Tan

@FrankTan: Şerefe :) +1
Vivek

46

Bir diziyi başka bir dizinin önüne bekliyorsanız, sadece kullanmak daha verimlidir concat. Yani:

var newArray = values.concat(oldArray);

Ama bu hala eski Dizi boyutunda O (N) olacaktır. Yine de, oldArray üzerinden manuel olarak yinelemekten daha verimlidir. Ayrıca, ayrıntılara bağlı olarak, size yardımcı olabilir, çünkü birçok değeri yerleştirecekseniz, her birini ayrı ayrı eklemek yerine önce bir diziye koyup sonra oldArray'ı bitirmek daha iyidir.

OldArray boyutunda O (N) 'den daha iyi yapmanın bir yolu yoktur, çünkü diziler ilk eleman sabit bir konumda olacak şekilde bitişik bellekte saklanır. İlk öğeden önce eklemek istiyorsanız, diğer tüm öğeleri taşımanız gerekir. Bu sorunu çözmek için @GWW'nin söylediklerini yapın ve bağlantılı bir liste veya farklı bir veri yapısı kullanın.


2
Ah evet, unuttum unshift. Ancak a) oldArray'ı değiştirirken concat(hangisinin sizin için daha iyi olduğu duruma bağlıdır) ve b) yalnızca bir öğe eklediğine dikkat edin.
mgiuca

1
Vay canına, bu çok daha yavaş. Söylediğim gibi, dizinin bir kopyasını yapıyor (ve ayrıca yeni bir dizi oluşturuyor [0]), ancak unshift bunu yerinde mutasyona uğratıyor. Ancak her ikisi de O (N) olmalıdır. Ayrıca bu siteye bağlantı için alkış - çok kullanışlı görünüyor.
mgiuca

2
concat diziyi döndürdüğü ve unshift yeni uzunluğu döndürdüğü için oneliner için iyi
Z. Khullah

32

Diziyi (a1 dizisiyle a1) başa eklemek isterseniz aşağıdakileri kullanabilirsiniz:

var a1 = [1, 2];
var a2 = [3, 4];
Array.prototype.unshift.apply(a1, a2);
console.log(a1);
// => [3, 4, 1, 2]

6

f eski diziyi korumanız, eskisini dilimlemeniz ve yeni değerleri dilimin başlangıcına kaydırmanız gerekir.

var oldA=[4,5,6];
newA=oldA.slice(0);
newA.unshift(1,2,3)

oldA+'\n'+newA

/*  returned value:
4,5,6
1,2,3,4,5,6
*/

4

Farklı ön hazırlık yöntemlerine ilişkin bazı yeni testlerim var. Küçük diziler (<1000 elem) için lider, bir itme yöntemiyle birleştirilmiş döngü içindir. Büyük diziler için, Unshift yöntemi lider olur.

Ancak bu durum yalnızca Chrome tarayıcı için geçerlidir. Firefox'ta unshift harika bir optimizasyona sahiptir ve her durumda daha hızlıdır.

ES6 forma tüm tarayıcılarda 100+ kat daha yavaştır.

https://jsbench.me/cgjfc79bgx/1


Mükemmel cevap! Ancak bir kısmını kaybetmemenizi sağlamak için, örneğin jsbench'in kaybolması durumunda test örneklerinizi burada (belki birkaç örnek çalıştırma sonucu ile) yeniden oluşturabilirsiniz.
ruffin

3

Özel bir yöntem var:

a.unshift(value);

Ancak, diziye birkaç öğeyi eklemek istiyorsanız, böyle bir yöntemi kullanmak daha hızlı olacaktır:

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

function prependArray(a, b) {
    var args = b;
    args.unshift(0);
    args.unshift(0);
    Array.prototype.splice.apply(a, args);
}

prependArray(a, b);
console.log(a); // -> [4, 5, 1, 2, 3]

2
Hayır ... unshift, ilk bağımsız değişken olarak dizi ekler: var a = [4, 5]; a.unshift ([1,2,3]); console.log (a) ' // -> [[4, 5], 1, 2, 3]
bjornd

4
Haklısın! Kullanmanız gerekirArray.prototype.unshift.apply(a,b);
david

1

Yerinde bekleyen örnek:

var A = [7,8,9]
var B = [1,2,3]

A.unshift(...B)

console.log(A) // [1,2,3,7,8,9]


1

Aramak unshiftyalnızca yeni dizinin uzunluğunu döndürür. Yani, başlangıçta bir öğe eklemek ve yeni bir dizi döndürmek için bunu yaptım:

let newVal = 'someValue';
let array = ['hello', 'world'];
[ newVal ].concat(array);

ya da sadece spread operatörü ile:

[ newVal, ...array ]

Bu şekilde, orijinal diziye dokunulmaz.

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.