Join the Stack Overflow Community
Stack Overflow is a community of 6.6 million programmers, just like you, helping each other.
Join them; it only takes a minute:
Sign up

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? Using jQuery would be great.

share|improve this question
1  
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 '16 at 13:32
    
jsben.ch/#/wQ9RU <= this benchmark gives an overview over the different ways to copy an array – EscapeNetscape Oct 24 '16 at 18:47

17 Answers 17

up vote 1407 down vote accepted

Use this:

var newArray = oldArray.slice();

Basically, the slice() operation clones the array and returns the reference to the 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.

share|improve this answer
3  
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 '12 at 18:46
34  
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 Burkett Jan 20 '14 at 16:29
1  
For readability I'd use Lodash or Underscore arr2 = _.clone(arr1) – Gábor Imre Oct 14 '14 at 11:30
10  
@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 '16 at 19:47
4  
@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 '16 at 19:48

In Javascript, deep-copy techniques depends 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)
var booleanLiteral = true;
var numberLiteral = 1;
var stringLiteral = 'true';

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

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

From these elements we can create three types of arrays.

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

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

// 3) Array of prototype-objects (function)
var 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.

  • Array of literal-values (type1)
    The 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 is faster in performance than Concat (http://jsperf.com/duplicate-array-slice-vs-concat/3).

  • 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 jQuery $.extend(myArray) technique can be used to deep-copy all array-types. Libraries like Underscore and Lo-dash offer similar deep-copy functions to jQuery $.extend(), yet have slower performance. More surprisingly, $.extend() has faster performance than the JSON.parse(JSON.stringify(myArray)) technique http://jsperf.com/js-deep-copy/2.

Summarized into a table:

Javascript deep copy techniques by element types

And for those developers that shy away from third-party libraries (like jQuery), you can use the following custom function; which has faster performance than $.extend, and deep-copies all arrays.

function copy(o) {
   var output, v, key;
   output = Array.isArray(o) ? [] : {};
   for (key in o) {
       v = o[key];
       output[key] = (typeof v === "object") ? copy(v) : v;
   }
   return output;
}

Warning: In NodeJS this solution is problematic; any null elements in the deep copied array will be converted to empty object literals {}.

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 has the fastest performance.

// Fastest performance for deep copying literal values
arr2 = arr1.slice();

// Any of these techniques will deep copy literal values as well,
// but with slower performance
arr2 = arr1.splice(0);
arr2 = arr1.concat();
arr2 = JSON.parse(JSON.stringify(arr1));
arr2 = $.extend(true, [], arr1); // jQuery.js needed
arr2 = _.extend(arr1); // Underscore.js needed
arr2 = _.cloneDeep(arr1); // Lo-dash.js needed
arr2 = copy(arr1); // Custom-function needed - as provided above
share|improve this answer
7  
Saket's answer doesn't use splice it uses slice. Very different – James Montagne May 16 '14 at 20:53
1  
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 '14 at 19:53
4  
I think this should be the right answer for the question. slice, concat have issues – Manu M Dec 17 '14 at 12:09
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 '15 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. – tfmontague May 21 '15 at 9:35

No jQuery needed... Working Example

var arr2 = arr1.slice()

This copys the array from the starting position 0 through the end of the array.

It is important to note that it will work as expected for primitive types (string, number, etc.), and to also explain the expected behavior for reference types...

If you have an array of Reference types, say of type Object. The array will be copied, but both of the arrays will contain references to the same Object's. So in this case it would seem like the array is copied by reference even though the array is actually copied.

share|improve this answer
    
Thanks for the example. I'm curious, is this kind of copying called a deep copy? – ayjay Oct 14 '14 at 20:03
3  
No this would not be a deep copy. – jondavidjohn Oct 14 '14 at 22:16

An alternative to slice is concat, which can be used in 2 ways. The first of these is perhaps more readable as the intended behaviour is very clear:

var array2 = [].concat(array1);

The second method is:

var array2 = array1.concat();

Cohen (in the comments) pointed out that this latter method has better performance.

The way this works is that the concat method creates a new array consisting of the elements in the object on which it is called followed by the elements of any arrays passed to it as arguments. So when no arguments are passed, it simply copies the array.

Lee Penkman, also in the comments, points out that if there's a chance array1 is undefined, you can return an empty array as follows:

var array2 = [].concat(array1 || []);

Or, for the second method:

var array2 = (array1 || []).concat();

Note that you can also do this with slice: var array2 = (array1 || []).slice();.

share|improve this answer
28  
Actually you can also do: var array2 = array1.concat(); It's a lot faster regarding performance. (JSPerf: jsperf.com/copy-simple-array and jsperf.com/copy-array-slice-vs-concat/5 – Cohen Dec 19 '12 at 18:50
4  
Its worth noting that if array1 isn't an array then [].concat(array1) returns [array1] e.g. if its undefined you'll get [undefined]. I sometimes do var array2 = [].concat(array1 || []); – lee penkman Aug 1 '14 at 8:27
    
Concat doesn't create a reference to the object but creates a new array. This is what I needed – Shyamal Parikh Oct 1 '15 at 8:38

In the future (ES6) 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'];

If you want to start using this now you could use typescript or babel and compile to safe javascript.

More info on spreads

share|improve this answer
4  
This should be upvoted – SobiborTreblinka May 25 '16 at 2:36
1  
Hooray for ES6 answer. – Reid Blomquist Jun 13 '16 at 17:00

This is how I've done it after trying many approaches:

var newArray = JSON.parse(JSON.stringify(orgArray));

This will create a new deep copy not related to the first one (not a shallow copy).

Also this obviously will not clone events and functions, but the good thing you can do it in one line, and it can be used for any kind of object (arrays, strings, numbers, objects ...)

share|improve this answer
2  
This is the best one. I use the same method a long time ago and think that there is no more sense in old school recursive loops – Vladimir Kharlampidi May 5 '14 at 20:28
1  
Be aware that this option doesn't handle well graph-like structures: crashes in presence of cycles, and doesn't preserve shared references. – Ruben Jun 28 '14 at 23:12
1  
This also fails for things like Date, or indeed, anything that has a prototype. In addition, undefineds get converted to nulls. – Dancrumb Sep 18 '14 at 19:57
3  
Is no one brave enough to comment on the gross inefficiency in both CPU and memory of serializing to text and then parsing back to an object? – Lawrence Dol Dec 9 '14 at 23:58
1  
This solution is the only one that worked. Using slice() is really a fake solution. – user3888372 Sep 28 '15 at 11:52

Some of mentioned methods work well when working with simple data types like number or string, but when the array contains other objects these methods fail. When we try to pass any object from one array to another it is passed as a reference, not the object.

Add the following code in your JavaScript file:

Object.prototype.clone = function() {
    var newObj = (this instanceof Array) ? [] : {};
    for (i in this) {
        if (i == 'clone') 
            continue;
        if (this[i] && typeof this[i] == "object") {
            newObj[i] = this[i].clone();
        } 
        else 
            newObj[i] = this[i]
    } return newObj;
};

And simply use

var arr1 = ['val_1','val_2','val_3'];
var arr2 = arr1.clone()

It will work.

share|improve this answer
2  
i get this error when i add this code to my page 'Uncaught RangeError: Maximum call stack size exceeded' – sawe Jan 21 '13 at 17:56
    
On Which Browser did you see this error?? – sarvesh singh Apr 10 '13 at 12:12
1  
My apologies, this error occurs in chrome if arr1 is not declared. so i copy-pasted the above code, and i get the error, however, if i declare the array arr1, then i do not get the error. You could improve the answer by declaring arr1 just above arr2, i see there are quite a few of 'us' out there who did not recognise that we had to declare arr1 (partly because when i was evaluating your answer, i was in a rush and needed something that 'just works') – sawe Apr 11 '13 at 5:01
    
.slice() still works fine even if you have objects in your array: jsfiddle.net/edelman/k525g – Jason May 30 '13 at 19:49
6  
@Jason but the objects are still pointing to the same object so changing one will change the other. jsfiddle.net/k525g/1 – Samuel Jul 8 '13 at 14:39

From ES2015,

var arr2 = [...arr1];
share|improve this answer
    
This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post. - From Review – Bartłomiej Semańczyk Dec 24 '15 at 7:51
1  
Please be careful when answering old questions which already have many responses, including an accepted one. You need to explain why your answer is better than any of the existing ones. – APC Dec 24 '15 at 7:58

Adding to the solution of array.slice(); be aware that if you have multidimensional array sub-arrays will be copied by references. What you can do is to loop and slice() each sub-array individually

var arr = [[1,1,1],[2,2,2],[3,3,3]];
var arr2 = arr.slice();

arr2[0][1] = 55;
console.log(arr2[0][1]);
console.log(arr[0][1]);

function arrCpy(arrSrc, arrDis){
 for(elm in arrSrc){
  arrDis.push(arrSrc[elm].slice());
}
}

var arr3=[];
arrCpy(arr,arr3);

arr3[1][1] = 77;

console.log(arr3[1][1]);
console.log(arr[1][1]);

same things goes to array of objects, they will be copied by reference, you have to copy them manually

share|improve this answer

I personally think Array.from is a more readable solution. By the way, just beware of its browser support.

//clone
let x = [1,2,3];
let y = Array.from(x);

//deep clone
let clone = arr => Array.from(arr,item => Array.isArray(item) ? clone(item) : item);
let x = [1,[],[[]]];
let y = clone(x);
share|improve this answer
1  
Yes, this is very readable. The .slice() solution is completely unintuitive. Thanks for this. – Banago Jul 27 '16 at 14:46

If you are in an environment of ECMAScript 6, using the Spread Operator you could do it this way:

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

console.log(arr1)
console.log(arr2)
<script src="http://www.wzvang.com/snippet/ignore_this_file.js"></script>

share|improve this answer

In my particular case I needed to ensure the array remained intact so this worked for me:

// Empty array
arr1.length = 0;
// Add items from source array to target array
for (var i = 0; i < arr2.length; i++) {
    arr1.push(arr2[i]);
}
share|improve this answer
1  
+1 for not adding obscuity to your code by calling a function that does exactly the same thing, but in a less obvious way. slice may be more efficient under the hood, but to anyone working on the code, this shows your intent. plus it makes it easier to optimise later, if you want to (for example) filter what you are copying. note however this does not handle deep copying, and the same internal objects are passed to the new array, by reference. This might be what you want to do, it might not. – unsynchronized Jun 30 '14 at 22:45

Make copy of multidimensional array/object:

function deepCopy(obj) {
   if (Object.prototype.toString.call(obj) === '[object Array]') {
      var out = [], i = 0, len = obj.length;
      for ( ; i < len; i++ ) {
         out[i] = arguments.callee(obj[i]);
      }
      return out;
   }
   if (typeof obj === 'object') {
      var out = {}, i;
      for ( i in obj ) {
         out[i] = arguments.callee(obj[i]);
      }
      return out;
   }
   return obj;
}

Thanks to James Padolsey for this function.

Source: http://james.padolsey.com/javascript/deep-copying-of-objects-and-arrays/

share|improve this answer
1  
Nice , helped me. thank you. – f.n174 Jan 2 '15 at 7:57

If you want to make a new copy of an object or array, you must explicitly copy the properties of the object or the elements of the array, for example:

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

for (var i=0; i < arr1.length; i++) {
   arr2[i] = arr1[i];
}

You can search for more information on Google about immutable primitive values and mutable object references.

share|improve this answer
1  
You don't have to explicitly copy the properties of the objects of the array. See Chtiwi Malek's answer. – Magne Jun 4 '14 at 14:30

Here's a variant:

var arr1=['a', 'b', 'c'];
var arr2=eval(arr1.toSource());
arr2.push('d');
console.log('arr1: '+arr1+'\narr2: '+arr2);
/*
 *  arr1: a,b,c
 *  arr2: a,b,c,d
 */
share|improve this answer
    
not such a bad idea, though I'd better use JSON stringify/parse instead of eval, and yet another jsPerf compare would be good to check out, also note toSource is not standard and will not work in Chrome for example. – dmi3y Mar 24 '14 at 19:23

There's the newly introduced Array.from, but unfortunately, as of the time of this writing it's only supported on recent Firefox versions (32 and higher). It can be simply used as follows:

var arr1 = [1, 2, 3];
console.log(Array.from(arr1)); // Logs: [1, 2, 3]

Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from

Or Array.prototype.map may be used with an identity function:

function identity(param)
{
    return param;
}

var arr1 = [1, 2, 3],
    clone = arr1.map(identity);

Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

share|improve this answer

Using jQuery deep copy could be made as following:

var arr2 = $.extend(true, [], arr1);
share|improve this answer

protected by Tushar Gupta Jul 30 '14 at 12:24

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.