Fastest way to duplicate an array in JavaScript - slice vs. 'for' loop

Viewed 737681

In order to duplicate an array in JavaScript: Which of the following is faster to use?

Slice method

var dup_array = original_array.slice();

For loop

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

I know both ways do only a shallow copy: if original_array contains references to objects, objects won't be cloned, but only the references will be copied, and therefore both arrays will have references to the same objects. But this is not the point of this question.

I'm asking only about speed.

25 Answers

Fastest Way to Clone an Array

I made this very plain utility function to test the time that it takes to clone an array. It is not 100% reliable however it can give you a bulk idea as for how long it takes to clone an existing array:

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

And tested different approach:

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:

  1. Tests were made back in 2018, so today most likely you'll get different result with current browsers.
  2. Out of all of those, the only way to deep clone an array is by using JSON.parse(JSON.stringify(arr)).

    That said, do not use the above if your array might include functions as it will return null.
    Thank you @GilEpshtain for this update.

I ran @mesqueeb's Benchmark in Chrome and Firefox. slice() still seems to be a quite good option.

Chrome 95.0: Chrome Array duplication benchmark

Firefox 94.0: Firefox Array duplication benchmark

var cloned_array = [].concat(target_array);

Fastest way to clone an Array of Objects will be using spread operator

var clonedArray=[...originalArray]
or
var clonedArray = originalArray.slice(0); //with 0 index it's little bit faster than normal slice()

but the objects inside that cloned array will still pointing at the old memory location. hence change to clonedArray objects will also change the orignalArray. So

var clonedArray = originalArray.map(({...ele}) => {return ele})

this will not only create new array but also the objects will be cloned to.

disclaimer if you are working with nested object in that case spread operator will work as SHALLOW CLONE. At that point better to use

var clonedArray=JSON.parse(JSON.stringify(originalArray));

Benchmark time!

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>

The benchmark will run for 10s since you click the button.

My results:

Chrome (V8 engine):

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 Engine):

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)

Winner code:

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

Winner engine:

SpiderMonkey (Mozilla/Firefox)

In ES6, you can simply utilize the Spread syntax.

Example:

let arr = ['a', 'b', 'c'];
let arr2 = [...arr];

Please note that the spread operator generates a completely new array, so modifying one won't affect the other.

Example:

arr2.push('d') // becomes ['a', 'b', 'c', 'd']
console.log(arr) // while arr retains its values ['a', 'b', 'c']

There were several ways to clone an array. Basically, Cloning was categorized in two ways:

  1. Shallow copy
  2. Deep copy

Shallow copies only cover the 1st level of the array and the rest are referenced. If you want a true copy of nested elements in the arrays, you’ll need a deep clone.

Example :

const arr1 = [1,2,3,4,5,6,7]           
// Normal Array (shallow copy is enough)     
const arr2 = [1,2,3,[4],[[5]],6,7]          
// Nested Array  (Deep copy required) 


Approach 1 : Using (...)Spread Operator  (Shallow copy enough)
const newArray = [...arr1] // [1,2,3,4,5,6,7]

Approach 2 : Using Array builtIn Slice method (Deep copy)  
const newArray = arr1.slice()  // [1,2,3,4,5,6,7]

Approach 3 : Using Array builtIn Concat method (Deep a copy)
const newArray = [].concat(arr1)  // [1,2,3,4,5,6,7]

Approach 4 : Using JSON.stringify/parse. (Deep a copy & fastest)
const newArray = JSON.parse(JSON.stringify(arr2));)  // [1,2,3,[4],[[5]],6,7]

Approach 5: Using own recursive function or using loadash's __.cloneDeep method. (Deep copy)
        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 ] ]

So, in order to avoid these scenarios to happen, use

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

        const cloneArr = Array.from(arr);

Fast ways to duplicate an array in JavaScript in Order:

#1: array1copy = [...array1];

#2: array1copy = array1.slice(0);

#3: array1copy = array1.slice();

If your array objects contain some JSON-non-serializable content (functions, Number.POSITIVE_INFINITY, etc.) better to use

array1copy = JSON.parse(JSON.stringify(array1))

You can follow this code. Immutable way array clone. This is the perfect way to array cloning


const array = [1, 2, 3, 4]

const newArray = [...array]
newArray.push(6)
console.log(array)
console.log(newArray)

If you want a REAL cloned object/array in JS with cloned references of all attributes and sub-objects:

export function clone(arr) {
    return JSON.parse(JSON.stringify(arr))
}

ALL other operations do not create clones, because they just change the base address of the root element, not of the included objects.

Except you traverse recursive through the object-tree.

For a simple copy, these are OK. For storage address relevant operations I suggest (and in most all other cases, because this is fast!) to type convert into string and back in a complete new object.

If you are taking about slice it is used to copy elements from an array and create a clone with same no. of elements or less no. of elements.

var arr = [1, 2, 3 , 4, 5];

function slc() {
  var sliced = arr.slice(0, 5);
// arr.slice(position to start copying master array , no. of items in new array)
  console.log(sliced);
}
slc(arr);

Related