JavaScriptで配列を複製するには、次のうちどれを使用するのが速いですか?
var dup_array = original_array.slice();
For
ループfor(var i = 0, len = original_array.length; i < len; ++i)
dup_array[i] = original_array[i];
両方の方法で 浅いコピーしかできないことを知っています :original_arrayにオブジェクトへの参照が含まれている場合、オブジェクトは複製されず、参照だけがコピーされます。しかし、これはこの質問のポイントではありません。
私はスピードについてだけ聞いています。
配列を複製するには、少なくとも 5 (!)の方法があります。
BENCHMARKSスレッド があり、以下の情報を提供しています。
blink browsersの場合slice()
が最速の方法、concat()
が少し遅く、そしてwhile loop
が2.4倍遅くなります。
他のブラウザではwhile loop
が最速の方法です、なぜならそれらのブラウザはslice
とconcat
のための内部最適化を持っていないからです。
これは2016年7月にも当てはまります。
以下はブラウザのコンソールにコピー&ペーストして画像を見るために数回実行することができる簡単なスクリプトです。それらはミリ秒を出力します、低い方が良いです。
whileループ
n = 1000*1000;
start = + new Date();
a = Array(n);
b = Array(n);
i = a.length;
while(i--) b[i] = a[i];
console.log(new Date() - start);
スライス
n = 1000*1000;
start = + new Date();
a = Array(n);
b = a.slice();
console.log(new Date() - start);
これらのメソッドはArrayオブジェクト自体を複製しますが、配列の内容は参照によってコピーされるため、詳細複製はされません。
origAr == clonedArr //returns false
origAr[0] == clonedArr[0] //returns true
技術的にはslice
が 最速です。 しかし 、0
beginインデックスを追加するとさらに速くなります。
myArray.slice(0);
より速いです
myArray.slice();
eS6の方法はどうですか?
arr2 = [...arr1];
ArrayまたはObjectをディープクローンする最も簡単な方法:
var dup_array = JSON.parse(JSON.stringify(original_array))
var cloned_array = [].concat(target_array);
私は簡単なデモをまとめました: http://jsbin.com/agugo3/edit
Internet Explorer 8での結果は156、782、および750です。これは、この場合、slice
がはるかに速いことを示しています。
a.map(e => e)
はこの仕事のもう一つの選択肢です。今日現在、.map()
はFirefoxでは非常に高速です(ほぼ.slice(0)
と同じくらい高速です)が、Chromeではそうではありません。
一方、配列が多次元の場合、配列はオブジェクトでオブジェクトは参照型なので、スライスメソッドや連結メソッドはどれも解決策にはなりません。したがって、配列を複製する適切な方法の1つはArray.prototype.clone()
の発明です。次のように。
Array.prototype.clone = function(){
return this.map(e => Array.isArray(e) ? e.clone() : e);
};
var arr = [ 1, 2, 3, 4, [ 1, 2, [ 1, 2, 3 ], 4 , 5], 6 ],
brr = arr.clone();
brr[4][2][1] = "two";
console.log(JSON.stringify(arr));
console.log(JSON.stringify(brr));
私はそれが配列をクローンするのにかかる時間をテストするためにこの非常に明白な効用関数を作りました。これは100%信頼できるわけではありませんが、既存のアレイのクローンを作成するのにかかる時間については、大まかな考えを与えることができます。
function clone(fn) {
const arr = [...Array(1000000)];
console.time('timer');
fn(arr);
console.timeEnd('timer');
}
1) 5.79ms -> clone(arr => Object.values(arr));
2) 7.23ms -> clone(arr => [].concat(arr));
3) 9.13ms -> clone(arr => arr.slice());
4) 24.04ms -> clone(arr => { const a = []; for (let val of arr) { a.Push(val); } return a; });
5) 30.02ms -> clone(arr => [...arr]);
6) 39.72ms -> clone(arr => JSON.parse(JSON.stringify(arr)));
7) 99.80ms -> clone(arr => arr.map(i => i));
8) 259.29ms -> clone(arr => Object.assign([], arr));
9) Maximum call stack size exceeded -> clone(arr => Array.of(...arr));
_更新_ :
注:これらすべてのうち、配列をディープクローンする唯一の方法はJSON.parse(JSON.stringify(arr))
を使用することです。
そうは言っても、あなたの配列がnull
を返すので関数に配列が含まれる可能性がある場合は上記を使わないでください。
このアップデートをありがとうございました 。
.slice()は二次元配列では動作しないことを忘れないでください。あなたはこのような機能が必要です。
function copy(array) {
return array.map(function(arr) {
return arr.slice();
});
}
link を見てください。スピードではなく快適さです。ご覧のとおり、 slice(0) on プリミティブ型 しか使用できません。
配列の複製ではなく、配列の独立したコピーを作成するには、配列スライス方式を使用できます。
例:
配列の複製ではなく、配列の独立したコピーを作成するには、配列スライス方式を使用できます。
var oldArray = ["mip", "map", "mop"]; var newArray = oldArray.slice();
オブジェクトをコピーまたは複製するには:
function cloneObject(source) { for (i in source) { if (typeof source[i] == 'source') { this[i] = new cloneObject(source[i]); } else{ this[i] = source[i]; } } } var obj1= {bla:'blabla',foo:'foofoo',etc:'etc'}; var obj2= new cloneObject(obj1);
出典: リンク
もっときれいな解決策があります。
var srcArray = [1, 2, 3];
var clonedArray = srcArray.length === 1 ? [srcArray[0]] : Array.apply(this, srcArray);
Array
コンストラクタは、引数を1つだけ指定して呼び出された場合とは動作が異なるため、長さチェックが必要です。
@Danが "この答えは早く時代遅れになる。 benchmarks を使って実際の状況をチェックする"と言ったように、jsperfからの答えがない具体的な答えが1つあります: while :
var i = a.length;
while(i--) { b[i] = a[i]; }
ランナーナップa.concat()
が578,129 ops /秒で960,589 ops /秒、これは60%です。
これは最新のFirefox(40)64ビットです。
@aleclarsonは、より信頼性の高い新しいベンチマークを作成しました。
配列の長さによって異なります。配列の長さが1,000,000以下の場合、slice
メソッドとconcat
メソッドはほぼ同じ時間がかかります。しかし、より広い範囲を指定すると、concat
メソッドが勝ちます。
たとえば、次のコードを試してください。
var original_array = [];
for(var i = 0; i < 10000000; i ++) {
original_array.Push( Math.floor(Math.random() * 1000000 + 1));
}
function a1() {
var dup = [];
var start = Date.now();
dup = original_array.slice();
var end = Date.now();
console.log('slice method takes ' + (end - start) + ' ms');
}
function a2() {
var dup = [];
var start = Date.now();
dup = original_array.concat([]);
var end = Date.now();
console.log('concat method takes ' + (end - start) + ' ms');
}
function a3() {
var dup = [];
var start = Date.now();
for(var i = 0; i < original_array.length; i ++) {
dup.Push(original_array[i]);
}
var end = Date.now();
console.log('for loop with Push method takes ' + (end - start) + ' ms');
}
function a4() {
var dup = [];
var start = Date.now();
for(var i = 0; i < original_array.length; i ++) {
dup[i] = original_array[i];
}
var end = Date.now();
console.log('for loop with = method takes ' + (end - start) + ' ms');
}
function a5() {
var dup = new Array(original_array.length)
var start = Date.now();
for(var i = 0; i < original_array.length; i ++) {
dup.Push(original_array[i]);
}
var end = Date.now();
console.log('for loop with = method and array constructor takes ' + (end - start) + ' ms');
}
a1();
a2();
a3();
a4();
a5();
Original_arrayの長さを1,000,000に設定した場合、slice
メソッドとconcat
メソッドはほぼ同じ時間(乱数に応じて3〜4ミリ秒)かかります。
Original_arrayの長さを10,000,000に設定した場合、slice
メソッドは60ミリ秒以上かかり、concat
メソッドは20ミリ秒以上かかります。
Spread
演算子を使用したECMAScript 2015の方法:
基本的な例:
var copyOfOldArray = [...oldArray]
var twoArraysBecomeOne = [...firstArray, ..seccondArray]
ブラウザコンソールで試してください。
var oldArray = [1, 2, 3]
var copyOfOldArray = [...oldArray]
console.log(oldArray)
console.log(copyOfOldArray)
var firstArray = [5, 6, 7]
var seccondArray = ["a", "b", "c"]
var twoArraysBecomOne = [...firstArray, ...seccondArray]
console.log(twoArraysBecomOne);
簡単な解決策:
original = [1,2,3]
cloned = original.map(x=>x)
const arr = ['1', '2', '3'];
// Old way
const cloneArr = arr.slice();
// ES6 way
const cloneArrES6 = [...arr];
// But problem with 3rd approach is that if you are using muti-dimensional
// array, then only first level is copied
const nums = [
[1, 2],
[10],
];
const cloneNums = [...nums];
// Let's change the first item in the first nested item in our cloned array.
cloneNums[0][0] = '8';
console.log(cloneNums);
// [ [ '8', 2 ], [ 10 ], [ 300 ] ]
// NOOooo, the original is also affected
console.log(nums);
// [ [ '8', 2 ], [ 10 ], [ 300 ] ]
それで、これらのシナリオが起こるのを避けるためには、
const arr = ['1', '2', '3'];
const cloneArr = Array.from(arr);
ベンチマーク時間!
function log(data) {
document.getElementById("log").textContent += data + "\n";
}
benchmark = (() => {
time_function = function(ms, f, num) {
var z = 0;
var t = new Date().getTime();
for (z = 0;
((new Date().getTime() - t) < ms); z++)
f(num);
return (z)
}
function clone1(arr) {
return arr.slice(0);
}
function clone2(arr) {
return [...arr]
}
function clone3(arr) {
return [].concat(arr);
}
Array.prototype.clone = function() {
return this.map(e => Array.isArray(e) ? e.clone() : e);
};
function clone4(arr) {
return arr.clone();
}
function benchmark() {
function compare(a, b) {
if (a[1] > b[1]) {
return -1;
}
if (a[1] < b[1]) {
return 1;
}
return 0;
}
funcs = [clone1, clone2, clone3, clone4];
results = [];
funcs.forEach((ff) => {
console.log("Benchmarking: " + ff.name);
var s = time_function(2500, ff, Array(1024));
results.Push([ff, s]);
console.log("Score: " + s);
})
return results.sort(compare);
}
return benchmark;
})()
log("Starting benchmark...\n");
res = benchmark();
console.log("Winner: " + res[0][0].name + " !!!");
count = 1;
res.forEach((r) => {
log((count++) + ". " + r[0].name + " score: " + Math.floor(10000 * r[1] / res[0][1]) / 100 + ((count == 2) ? "% *winner*" : "% speed of winner.") + " (" + Math.round(r[1] * 100) / 100 + ")");
});
log("\nWinner code:\n");
log(res[0][0].toString());
<textarea rows="50" cols="80" style="font-size: 16; resize:none; border: none;" id="log"></textarea>
ボタンをクリックすると、ベンチマークは10秒間実行されます。
私の結果:
Chrome(V8エンジン):
1. clone1 score: 100% *winner* (4110764)
2. clone3 score: 74.32% speed of winner. (3055225)
3. clone2 score: 30.75% speed of winner. (1264182)
4. clone4 score: 21.96% speed of winner. (902929)
Firefox(SpiderMonkeyエンジン):
1. clone1 score: 100% *winner* (8448353)
2. clone3 score: 16.44% speed of winner. (1389241)
3. clone4 score: 5.69% speed of winner. (481162)
4. clone2 score: 2.27% speed of winner. (192433)
勝者コード:
function clone1(arr) {
return arr.slice(0);
}
勝者エンジン:
SpiderMonkey(Mozilla/Firefox)