241

Possible Duplicate:
What is the most efficient way to clone a JavaScript object?

I need to copy an (ordered, not associative) array of objects. I'm using jQuery. I initially tried

jquery.extend({}, myArray)

but, naturally, this gives me back an object, where I need an array (really love jquery.extend, by the way).

So, what's the best way to copy an array?

Community
  • 1
  • 1
morgancodes
  • 25,055
  • 38
  • 135
  • 187

8 Answers8

293

Since Array.slice() does not do deep copying, it is not suitable for multidimensional arrays:

var a =[[1], [2], [3]];
var b = a.slice();

b.shift().shift();
// a is now [[], [2], [3]]

Note that although I've used shift().shift() above, the point is just that b[0][0] contains a pointer to a[0][0] rather than a value.

Likewise delete(b[0][0]) also causes a[0][0] to be deleted and b[0][0]=99 also changes the value of a[0][0] to 99.

jQuery's extend method does perform a deep copy when a true value is passed as the initial argument:

var a =[[1], [2], [3]];
var b = $.extend(true, [], a);

b.shift().shift();
// a is still [[1], [2], [3]]
Noah Sussman
  • 4,576
  • 2
  • 28
  • 25
  • 14
    Thanks Noah. Looks like my biggest problem was that I was giving $.extend and object as its first argument, not an array. – morgancodes May 03 '09 at 15:32
  • 2
    Can you explain what is the purpose of b.shift().shift() here? – LCJ Feb 14 '12 at 11:32
  • 1
    b is just being manipulated to show that a and b don't hold the same value. Shouldn't a and b be represented as [[3]]? Shift removes the first value in an array entirely whether it is of any type. It doesn't perform a recursive search for a primitive type and then remove that. The length of an array that holds the context for the method always decreases by 1 and is edited in place. – danronmoon Jun 05 '12 at 02:27
  • Thank you! Your line: var b = $.extend(true, [], a); saved my life! I used $.extend(true, {}, a) braces instead of square brackets! – Mixim Mar 11 '15 at 14:34
  • 1
    thanks it helped very much , normally when we copy array by direct assignment way , when one array is changed then other copied array also get changes , so this method do real copy. – user889030 Sep 01 '17 at 12:49
31

$.extend(true, [], [['a', ['c']], 'b'])

That should do it for you.

vsync
  • 118,978
  • 58
  • 307
  • 400
geowa4
  • 40,390
  • 17
  • 88
  • 107
21

I realize you're looking for a "deep" copy of an array, but if you just have a single level array you can use this:

Copying a native JS Array is easy. Use the Array.slice() method which creates a copy of part/all of the array.

var foo = ['a','b','c','d','e'];
var bar = foo.slice();

now foo and bar are 5 member arrays of 'a','b','c','d','e'

of course bar is a copy, not a reference... so if you did this next...

bar.push('f');
alert('foo:' + foo.join(', '));
alert('bar:' + bar.join(', '));

you would now get:

foo:a, b, c, d, e
bar:a, b, c, d, e, f
scunliffe
  • 62,582
  • 25
  • 126
  • 161
13

Everything in JavaScript is pass by reference, so if you want a true deep copy of the objects in the array, the best method I can think of is to serialize the entire array to JSON and then de-serialize it back.

7

If you want to use pure JavaScript then try this:

 var arr=["apple","ball","cat","dog"];
 var narr=[];

 for(var i=0;i<arr.length;i++){
     narr.push(arr[i]);
 }
 alert(narr); //output: apple,ball,vat,dog
 narr.push("elephant");
 alert(arr); // output: apple,ball,vat,dog
 alert(narr); // apple,ball,vat,dog,elephant
Ryan Berger
  • 9,644
  • 6
  • 44
  • 56
frends
  • 71
  • 1
  • 1
3

how about complex types? when array contains objects... or any else

My variant:

Object.prototype.copy = function(){
    var v_newObj = {};
    for(v_i in this)
        v_newObj[v_i] = (typeof this[v_i]).contains(/^(array|object)$/) ? this[v_i].copy() : this[v_i];
    return v_newObj;
}

Array.prototype.copy = function(){
    var v_newArr = [];
    this.each(function(v_i){
        v_newArr.push((typeof v_i).contains(/^(array|object)$/) ? v_i.copy() : v_i);
    });
    return v_newArr;
}

It's not final version, just an idea.

PS: method each and contains are prototypes also.

Xrumet
  • 129
  • 1
  • 4
2

I plan on releasing this code in the next version of jPaq, but until then, you can use this if your goal is to do a deep copy of arrays:

Array.prototype.clone = function(doDeepCopy) {
    if(doDeepCopy) {
        var encountered = [{
            a : this,
            b : []
        }];

        var item,
            levels = [{a:this, b:encountered[0].b, i:0}],
            level = 0,
            i = 0,
            len = this.length;

        while(i < len) {
            item = levels[level].a[i];
            if(Object.prototype.toString.call(item) === "[object Array]") {
                for(var j = encountered.length - 1; j >= 0; j--) {
                    if(encountered[j].a === item) {
                        levels[level].b.push(encountered[j].b);
                        break;
                    }
                }
                if(j < 0) {
                    encountered.push(j = {
                        a : item,
                        b : []
                    });
                    levels[level].b.push(j.b);
                    levels[level].i = i + 1;
                    levels[++level] = {a:item, b:j.b, i:0};
                    i = -1;
                    len = item.length;
                }
            }
            else {
                levels[level].b.push(item);
            }

            if(++i == len && level > 0) {
                levels.pop();
                i = levels[--level].i;
                len = levels[level].a.length;
            }
        }

        return encountered[0].b;
    }
    else {
        return this.slice(0);
    }
};

The following is an example of how to call this function to do a deep copy of a recursive array:

// Create a recursive array to prove that the cloning function can handle it.
var arrOriginal = [1,2,3];
arrOriginal.push(arrOriginal);

// Make a shallow copy of the recursive array.
var arrShallowCopy = arrOriginal.clone();

// Prove that the shallow copy isn't the same as a deep copy by showing that
// arrShallowCopy contains arrOriginal.
alert("It is " + (arrShallowCopy[3] === arrOriginal)
    + " that arrShallowCopy contains arrOriginal.");

// Make a deep copy of the recursive array.
var arrDeepCopy = arrOriginal.clone(true);

// Prove that the deep copy really works by showing that the original array is
// not the fourth item in arrDeepCopy but that this new array is.
alert("It is "
    + (arrDeepCopy[3] !== arrOriginal && arrDeepCopy === arrDeepCopy[3])
    + " that arrDeepCopy contains itself and not arrOriginal.");

You can play around with this code here at JS Bin.

Chris West
  • 885
  • 8
  • 17
  • 1
    Interesting. @Chris West can you say what issues that your answer addressees that jQuery extend does not? Many thanks. – iainH Aug 10 '14 at 07:03
2

I've come across this "deep object copy" function that I've found handy for duplicating objects by value. It doesn't use jQuery, but it certainly is deep.

http://www.overset.com/2007/07/11/javascript-recursive-object-copy-deep-object-copy-pass-by-value/

Diodeus - James MacFarlane
  • 112,730
  • 33
  • 157
  • 176
  • Thanks, was looking for a catch-all clone (sometimes have an object, sometimes an array of objects). – Apex Oct 16 '09 at 14:50