web-dev-qa-db-fra.com

Le moyen le plus rapide de dupliquer un tableau en JavaScript - Slice vs. 'for' loop

Pour dupliquer un tableau en JavaScript: lequel des éléments suivants est le plus rapide à utiliser?

Méthode Slice

var dup_array = original_array.slice();

Boucle For

for(var i = 0, len = original_array.length; i < len; ++i)
   dup_array[i] = original_array[i];

Je sais que les deux méthodes ne font qu'un copie superficielle: si original_array contient des références à des objets, les objets ne seront pas clonés, mais seules les références seront copiées. Par conséquent, les deux tableaux auront des références aux mêmes objets. . Mais ce n'est pas le but de cette question.

Je demande seulement à propos de la vitesse.

568
Marco Demaio

Il y a au moins 5 (!) Façons de cloner un tableau:

  • loop
  • slice
  • Array.from ()
  • concat
  • opérateur de propagation (FASTEST)

Il y a eu un huuuge thread BENCHMARKS , fournissant les informations suivantes:

  • pour clignotement des navigateurs slice() est la méthode la plus rapide, concat() est un peu plus lent et while loop est 2,4x Ralentissez.

  • pour les autres navigateurs, while loop est la méthode la plus rapide, car ces navigateurs ne disposent pas d’optimisations internes pour slice et concat.

Cela reste vrai en juillet 2016.

Vous trouverez ci-dessous des scripts simples que vous pouvez copier-coller dans la console de votre navigateur et exécuter plusieurs fois pour voir l'image. Ils produisent des millisecondes, moins c'est mieux.

en boucle

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);

tranche

n = 1000*1000;
start = + new Date();
a = Array(n); 
b = a.slice();
console.log(new Date() - start);

Veuillez noter que ces méthodes vont cloner l'objet Array lui-même. Cependant, le contenu du tableau est copié par référence et n'est pas cloné en profondeur.

origAr == clonedArr //returns false
origAr[0] == clonedArr[0] //returns true
723
Dan

Techniquement, slice est le moyen le plus rapide. Cependant , il est encore plus rapide si vous ajoutez l'index 0 begin.

myArray.slice(0);

est plus rapide que

myArray.slice();

http://jsperf.com/cloning-arrays/

215
KingKongFrog

qu'en est-il Es6 Way?

arr2 = [...arr1];
118
Yukulélé

Meilleure façon de cloner en profondeur Array ou Object:

var dup_array = JSON.parse(JSON.stringify(original_array))
42
var cloned_array = [].concat(target_array);
27
Sajjad Shirazy

J'ai mis en place une démo rapide: http://jsbin.com/agugo3/edit

Mes résultats sur Internet Explorer 8 sont 156, 782 et 750, ce qui indiquerait que slice est beaucoup plus rapide dans ce cas.

25
lincolnk

a.map(e => e) est une autre alternative pour ce travail. À ce jour, .map() est très rapide (presque aussi rapide que .slice(0)) dans Firefox, mais pas dans Chrome.

D'un autre côté, si un tableau est multidimensionnel, puisque les tableaux sont des objets et que les objets sont des types de référence, aucune des méthodes slice ou concat ne sera un remède ... Un moyen approprié de cloner un tableau est donc une invention de Array.prototype.clone() comme suit.

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));
19
Redu

Le moyen le plus rapide de cloner un tableau

J'ai créé cette fonction utilitaire très simple pour tester le temps nécessaire pour cloner un tableau. Il n'est pas fiable à 100%, mais il peut vous donner une idée globale du temps nécessaire pour cloner un tableau existant:

function clone(fn) {
    const arr = [...Array(1000000)];
    console.time('timer');
    fn(arr);
    console.timeEnd('timer');
}

Et testé une approche différente:

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));

UPDATE:
Remarque: parmi eux, le seul moyen de cloner en profondeur un tableau consiste à utiliser JSON.parse(JSON.stringify(arr)).

Cela étant dit, n'utilisez pas ce qui précède si votre tableau peut inclure des fonctions car il renverra null.
Merci @GilEpshtain pour cette mise à jour .

11
Lior Elrom

N'oubliez pas que .slice () ne fonctionnera pas pour les tableaux à deux dimensions. Vous aurez besoin d'une fonction comme celle-ci:

function copy(array) {
  return array.map(function(arr) {
    return arr.slice();
  });
}
6
martinedwards

Jetez un oeil à: lien . Ce n'est pas une question de vitesse, mais de confort. En outre, comme vous pouvez le constater, vous ne pouvez utiliser que slice (0) sur les types primitifs .

Pour créer une copie indépendante d'un tableau plutôt qu'une copie de la référence, vous pouvez utiliser la méthode slice.

Exemple:

Pour créer une copie indépendante d'un tableau plutôt qu'une copie de la référence, vous pouvez utiliser la méthode slice.

var oldArray = ["mip", "map", "mop"];
var newArray = oldArray.slice();

Pour copier ou cloner un objet:

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);

Source: lien

6
Margus

Cela dépend du navigateur. Si vous regardez dans le blog Array.prototype.slice vs création manuelle d'un tablea, voici un guide approximatif des performances de chaque:

Enter image description here

Résultats:

Enter image description here

6
kyndigs

Il existe une solution beaucoup plus propre:

var srcArray = [1, 2, 3];
var clonedArray = srcArray.length === 1 ? [srcArray[0]] : Array.apply(this, srcArray);

La vérification de la longueur est requise, car le constructeur Array se comporte différemment lorsqu'il est appelé avec exactement un argument.

6
ciembor

Comme @Dan a déclaré: "Cette réponse devient rapidement obsolète. Utilisez tests de performance pour vérifier la situation actuelle", il existe une réponse spécifique de jsperf qui n'a pas eu de réponse pour elle-même: tandis que :

var i = a.length;
while(i--) { b[i] = a[i]; }

avait 960 589 ops/s avec le runnerup a.concat() à 578 129 ops/s, ce qui correspond à 60%.

C'est le dernier Firefox (40) 64 bits.


@aleclarson a créé une nouvelle référence plus fiable.

6
serv-inc

Cela dépend de la longueur du tableau. Si la longueur du tableau est <= 1 000 000, les méthodes slice et concat prennent à peu près le même temps. Mais lorsque vous donnez une plage plus large, la méthode concat gagne.

Par exemple, essayez ce code:

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();

Si vous définissez la longueur de original_array sur 1 000 000, les méthodes slice et concat prennent à peu près le même temps (3-4 ms, en fonction des nombres aléatoires).

Si vous définissez la longueur de original_array sur 10 000 000, la méthode slice prend plus de 60 ms et la méthode concat dépasse 20 ms.

5
Gor

ECMAScript 2015 avec l'opérateur Spread:

Exemples de base:

var copyOfOldArray = [...oldArray]
var twoArraysBecomeOne = [...firstArray, ..seccondArray]

Essayez dans la console du navigateur:

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);

Références

4
Marian07

Une solution simple:

original = [1,2,3]
cloned = original.map(x=>x)
3
Caio Santos
        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 ] ]

Donc, afin d'éviter que ces scénarios se produisent, utilisez

        const arr = ['1', '2', '3'];

        const cloneArr = Array.from(arr);
2
Anki

Temps repère!

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>

Le repère fonctionnera pendant 10 secondes puisque vous cliquez sur le bouton.

Mes résultats:

Chrome (moteur 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 (moteur 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)

Code du gagnant:

function clone1(arr) {
    return arr.slice(0);
}

Moteur gagnant:

SpiderMonkey (Mozilla/Firefox)

0
Zibri