Categories
arrays javascript

Copy array by value

2035

When copying an array in JavaScript to another array:

var arr1 = ['a','b','c'];
var arr2 = arr1;
arr2.push('d');  //Now, arr1 = ['a','b','c','d']

I realized that arr2 refers to the same array as arr1, rather than a new, independent array. How can I copy the array to get two independent arrays?

8

  • 4

    It looks like currently in Chrome 53 and Firefox 48 we have cool performance for slice and splice operations and new spread operator and Array.from have much slower implementation. Look at perfjs.fnfo

    – Pencroff

    Sep 16, 2016 at 13:32


  • jsben.ch/#/wQ9RU <= this benchmark gives an overview over the different ways to copy an array

    Oct 24, 2016 at 18:47

  • jsperf.com/flat-array-copy

    May 25, 2017 at 17:08

  • 37

    It’s 2017, so you might consider using ES6 features: let arr2 = [...arr1]; developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…

    – Hinrich

    Sep 22, 2017 at 8:30

  • 2

    Well when you state a = b; you actually tell the program to point in both cases to a same symbolic link in random access memory. And when a value at this symbolic link is changed it affects a and b… So if you use a spread operator a= [...b]; program will create an additional symbolic link to a different location in random access memory and you can then manipulate a and b independently.

    – 71GA

    Dec 15, 2019 at 20:49


3025

Use this:

let oldArray = [1, 2, 3, 4, 5];

let newArray = oldArray.slice();

console.log({newArray});

Basically, the slice() operation clones the array and returns a reference to a new array.

Also note that:

For references, strings and numbers (and not the actual object), slice() copies object references into the new array. Both the original and new array refer to the same object. If a referenced object changes, the changes are visible to both the new and original arrays.

Primitives such as strings and numbers are immutable, so changes to the string or number are impossible.

14

  • 9

    Regarding performance the following jsPerf tests actually show that var arr2 = arr1.slice() is just as fast as var arr2 = arr1.concat(); JSPerf: jsperf.com/copy-array-slice-vs-concat/5 and jsperf.com/copy-simple-array . The result of jsperf.com/array-copy/5 kind of surprised me to the point I am wondering if the test code is valid.

    – Cohen

    Dec 19, 2012 at 18:46


  • 112

    Even though this has already received a ton of upvotes, it deserves another because it properly describes references in JS, which is sort of rare, unfortunately.

    – Wayne

    Jan 20, 2014 at 16:29

  • 36

    @GáborImre you’d add an entire library simply for readability? Really? I’d just add a comment if I were that concerned for readability. See: var newArray = oldArray.slice(); //Clone oldArray to newArray

    – dudewad

    Feb 8, 2016 at 19:47


  • 13

    @GáborImre I get that, sure. But answering a specific engineering problem by including an entire library in my opinion is not helpful, it’s design bloat. I see developers do that a lot, and then you end up with a project that included an entire framework to replace having to write a single function. Just my M.O., though.

    – dudewad

    Feb 9, 2016 at 19:48

  • 13

    Lesson learned: Don’t confuse .slice() with .splice(), which gives you an empty array. Big difference.

    Feb 28, 2018 at 6:20

615

In Javascript, deep-copy techniques depend on the elements in an array. Let’s start there.

Three types of elements

Elements can be: literal values, literal structures, or prototypes.

// Literal values (type1)
const booleanLiteral = true;
const numberLiteral = 1;
const stringLiteral="true";

// Literal structures (type2)
const arrayLiteral = [];
const objectLiteral = {};

// Prototypes (type3)
const booleanPrototype = new Bool(true);
const numberPrototype = new Number(1);
const stringPrototype = new String('true');
const arrayPrototype = new Array();
const objectPrototype = new Object(); // or `new function () {}

From these elements we can create three types of arrays.

// 1) Array of literal-values (boolean, number, string) 
const type1 = [ true, 1, "true" ];

// 2) Array of literal-structures (array, object)
const type2 = [ [], {} ];

// 3) Array of prototype-objects (function)
const type3 = [ function () {}, function () {} ];

Deep copy techniques depend on the three array types

Based on the types of elements in the array, we can use various techniques to deep copy.

Deep copy techniques

Javascript deep copy techniques by element types

Benchmarks

https://www.measurethat.net/Benchmarks/Show/17502/0/deep-copy-comparison

  • Array of literal-values (type1)
    The [ ...myArray ], myArray.splice(0), myArray.slice(), and myArray.concat() techniques can be used to deep copy arrays with literal values (boolean, number, and string) only; where slice() has the highest performance in Chrome, and spread ... has the highest performance in Firefox.

  • Array of literal-values (type1) and literal-structures (type2)
    The JSON.parse(JSON.stringify(myArray)) technique can be used to deep copy literal values (boolean, number, string) and literal structures (array, object), but not prototype objects.

  • All arrays (type1, type2, type3)

    • The Lo-dash cloneDeep(myArray) or jQuery extend(true, [], myArray) techniques can be used to deep-copy all array-types. Where the Lodash cloneDeep() technique has the highest performance.
    • And for those who avoid third-party libraries, the custom function below will deep-copy all array-types, with lower performance than cloneDeep() and higher performance than extend(true).
function copy(aObject) {
  // Prevent undefined objects
  // if (!aObject) return aObject;

  let bObject = Array.isArray(aObject) ? [] : {};

  let value;
  for (const key in aObject) {

    // Prevent self-references to parent object
    // if (Object.is(aObject[key], aObject)) continue;
    
    value = aObject[key];

    bObject[key] = (typeof value === "object") ? copy(value) : value;
  }

  return bObject;
}

So to answer the question…

Question

var arr1 = ['a','b','c'];
var arr2 = arr1;

I realized that arr2 refers to the same array as arr1, rather than a new, independent array. How can I copy the array to get two independent arrays?

Answer

Because arr1 is an array of literal values (boolean, number, or string), you can use any deep copy technique discussed above, where slice() and spread ... have the highest performance.

arr2 = arr1.slice();
arr2 = [...arr1];
arr2 = arr1.splice(0);
arr2 = arr1.concat();
arr2 = JSON.parse(JSON.stringify(arr1));
arr2 = copy(arr1); // Custom function needed, and provided above
arr2 = _.cloneDeep(arr1); // Lo-dash.js needed
arr2 = jQuery.extend(true, [], arr1); // jQuery.js needed

13

  • 2

    Many of these approaches do not work well. Using the assignment operator means that you have to reassign the original literal value of arr1. It’s very rare that that’s going to be the case. Using splice obliterates arr1, so that’s not a copy at all. Using JSON will fail if any of the values in the array are Functions or have prototypes (such as a Date).

    – Dancrumb

    Sep 18, 2014 at 19:53


  • 1

    Why splice(0)? Shouldn’t it be slice() ? I think it’s supposed not to modify original array, which splice does. @JamesMontagne

    – helpse

    May 14, 2015 at 15:29

  • 2

    splice will create pointers to the elements in the original array (shallow copy). splice(0) will allocate new memory (deep copy) for elements in the array which are numbers or strings, and create pointers for all other element types (shallow copy). By passing a start value of zero to the splice function-method, it won’t splice any elements from the original array, and therefore it doesn’t modify it.

    May 21, 2015 at 9:35


  • 2

    Actually, there is only one type of array: an array of “somethings”. There is no difference between [0,"1",{2:3},function random() {return 4;}, [[5,6,7],[8,9,10],[11,12,13]]] and any other array.

    – wizzwizz4

    Apr 7, 2016 at 9:12

  • 1

    IMO these all have bad legibility. The fact that someone is meaning to clone an array is not apparent to me. What is wrong with calling it x.clone()?

    – Jonny

    May 16 at 6:25


213

You can use array spreads ... to copy arrays.

const itemsCopy = [...items];

Also if want to create a new array with the existing one being part of it:

var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];

Array spreads are now supported in all major browsers but if you need older support use typescript or babel and compile to ES5.

More info on spreads

2