配列に値を付加する最も効率的な方法


268

サイズがN(where N > 0)の配列があるとすると、配列の前にO(N + 1)ステップを必要としない効率的な方法はありますか?

コードでは、基本的に、私が現在行っていることは

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

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

  return newArray;
}

リンクされたリストとポインタが大好きなのと同じくらい、ネイティブのJSデータ型を使用して物事を行うためのより効果的な方法があるはずだと感じています
samccone

@samccone:ええ、私のコメントは無視してください。申し訳ありません。Java:P
GWWの

3
Java、JavaScript、C、またはPython。どの言語でもかまいません。配列とリンクリストの複雑さのトレードオフは同じです。リンクリストは、組み込みのクラスがないため(Javaとは異なり)、JSでは扱いにくいかもしれませんが、本当に必要なのがO(1)挿入時間である場合は、リンクリストが必要です。
mgiuca

1
それを複製する必要がありますか?
ライアンフローレンス2011年

1
クローンを作成する必要がある場合、元の配列を変更してコピーを作成しないため、unshiftは不適切です。
mgiuca

回答:


493

big-Oの点でより効率的かどうかはわかりませんが、確かにこのunshift方法を使用する方が簡潔です。

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

[編集]

このjsPerfベンチマークは、配列をインプレースで変更しても問題がなければ、unshift big-Oのパフォーマンスが異なる可能性があるにもかかわらず、少なくともいくつかのブラウザーでかなり高速であることを示しています。元の配列を実際に変更できない場合は、次のスニペットのようなものを実行しますが、ソリューションよりもかなり高速ではないようです。

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

[編集2]

完全を期すために、OPの例の代わりに次の関数を使用してprependArray(...)、Array unshift(...)メソッドを利用できます。

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
prependunshift」に電話することにしたのは誰ですか。
スコットスタッフォード

12
@ScottStafford unshiftは、このような配列操作に適しているようです(要素を物理的に移動します)。prependあなたが文字通り要素前に付けるリンクされたリストに、より適切でしょう。
CamilB 2013

12
unshiftは、シフトを補完する機能です。「追加」と呼ぶのは奇妙な選択です。アンシフトは、プッシュがポップするようにシフトすることです。
ロバート卿

9
このソリューションの唯一の問題。unshift()はその長さを返しませんか?この回答のように配列ではありませんか? w3schools.com/jsref/jsref_unshift.asp
iDVB

4
pushそして、unshiftの両方が含まれているuのに対し、popshift持っていません。
Qian Chen

70

ES6では、spread演算子を使用して、元の要素の前に新しい要素が挿入された新しい配列を作成できます。

// 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:パフォーマンス

この答えは、覚えやすく簡潔な代替構文を提示するためのものです。一部のベンチマーク(この他の回答を参照)によると、この構文は大幅に遅いことに注意してください。これらの操作の多くをループで実行しない限り、これはおそらく問題にはなりません。


4
これは2017年の時点で投票される必要があります。簡潔です。スプレッドを使用することにより、新しいストリームが返されます。これは、さらに変更をチェーンするのに役立ちます。また、純粋です(つまり、aとbは影響を受けません)
Simon

あなたはに比べて所要時間アプト言及didntのunshift
ShashankのVivek

@ShashankVivekに感謝します。この答えは、覚えやすく簡潔な代替構文を提示するためのものです。更新します。
フランクタン

@FrankTan:乾杯:) +1
Vivek

46

配列を別の配列の前に追加する場合は、を使用する方が効率的concatです。そう:

var newArray = values.concat(oldArray);

しかし、これはoldArrayのサイズではO(N)のままです。それでも、oldArrayを手動で反復するよりも効率的です。また、詳細によっては、多くの値を付加する場合、個別に付加するのではなく、最初にそれらを配列に入れてから、最後にoldArrayを連結する方がよいため、役立つ場合があります。

oldArrayのサイズはO(N)よりも良い方法はありません。配列は最初の要素が固定位置にある連続したメモリに格納されるためです。最初の要素の前に挿入する場合は、他のすべての要素を移動する必要があります。これを回避する方法が必要な場合は、@ GWWが言ったことを実行し、リンクリストまたは別のデータ構造を使用してください。


2
ああ、忘れましたunshift。ただし、a)oldArray concatは変更されますが変更されません(そのため、状況に応じてどちらが適切かは異なります)。b)​​1つの要素のみを挿入します。
mgiuca

1
うわー、それははるかに遅いです。まあ、私が言ったように、それは配列のコピーを作成しています(そして新しい配列も作成しています[0])が、unshiftはその場所を変更します。しかし、どちらもO(N)でなければなりません。また、そのサイトへのリンクを応援します-とても便利に見えます。
mgiuca

2
onelinerのための良い、連結は、新しい長さの配列を返し、アンシフト返すので
Z. Khullah

32

配列(a1と配列a2)を付加する場合は、次のように使用できます。

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

6

古い配列を保持する必要がある場合は、古い配列をスライスし、新しい値をスライスの先頭にシフトします。

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

先頭に追加するさまざまな方法のいくつかの新鮮なテストがあります。小さいアレイ(<1000エレム)の場合、リーダーはプッシュ方式と組み合わせたサイクル用です。巨大なアレイの場合、Unshiftメソッドが主流になります。

ただし、この状況は実際にはChromeブラウザでのみ発生します。Firefoxでは、unshiftは素晴らしい最適化を備えており、すべてのケースでより高速です。

ES6の拡散は、すべてのブラウザーで100倍以上遅くなります。

https://jsbench.me/cgjfc79bgx/1


正解です。ただし、その一部を失わないようにするために、jsbenchがなくなった場合などに備えて、ここでテストケースを再現することもできます(サンプルの実行結果がいくつかある場合もあります)。
ruffin

3

特別な方法があります:

a.unshift(value);

しかし、配列の前にいくつかの要素を追加したい場合は、そのようなメソッドを使用する方が高速です。

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
いいえ... unshiftは最初の引数として配列を追加します:var a = [4、5]; a.unshift([1,2,3]); console.log(a); //-> [[4、5]、1、2、3]
ビョルンドジュン

4
あなたは正しいです!使用する必要がありますArray.prototype.unshift.apply(a,b);
david


1

呼び出しunshiftは、新しい配列の長さのみを返します。したがって、最初に要素を追加して新しい配列を返すには、次のようにしました。

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

または単にspread演算子で:

[ newVal, ...array ]

このようにして、元の配列は変更されません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.