For a deep copy of a multidimensional JavaScript array, one level of depth is sufficient. Is this really true?

Note. I am just a beginner coder, so there might be a glaring error or confusion at the heart of this question.

Basically, I need to deeply copy multidimensional arrays "by value" in JavaScript to an unknown depth. I thought it would require some complicated recursion, but it looks like in JavaScript you only need to copy one level deep to copy the entire array by value.

As an example, here is my test code using a deliberately collapsed array.

function test() {
  var arr = [ ['ok1'],[],[ [],[],[ [], [ [ ['ok2'], [] ] ] ] ] ];
  var cloned = cloneArray(arr);
  arr = '';   // Delete the original
  alert ( cloned );
}


function cloneArray(arr) {  
  // Deep copy arrays. Going one level deep seems to be enough.
  var clone = [];
  for (i=0; i<arr.length; i++) {
    clone.push( arr[i].slice(0) )
  }
  return clone;
}

      

In my testing (latest stable Chrome and Firefox on Ubuntu) even the deepest parts of the array seem to be copied by value successfully in the clone, even after deleting the original, even though the slice () "copy" only went through one layer. Is this the default behavior in JavaScript? Can I depend on this to work in older browsers?

+3


source to share


4 answers


Your test is flawed for whether a true copy is being made, which makes your conclusion wrong that you are getting a complete copy of all data in the nested arrays. You only make a two-level copy, not a N-level copy.

Javascript is a garbage collector, so you don't actually remove variables or objects, and even if you tried to avoid affecting the same variable if it is referenced elsewhere in your code. To make sure you actually have a completely independent copy, try nesting the object two levels deep and then changing the object's property in the original array. You will find that the same object changes in the cloned array because you are not doing a deep clone. Both arrays refer to the same object.

Here's an example .

function cloneArray(arr) {  
  // Deep copy arrays. Going one level deep seems to be enough.
  var clone = [];
  for (i=0; i<arr.length; i++) {
    clone.push( arr[i].slice(0) )
  }
  return clone;
}

var x = [[{foo: 1}]];

var y = cloneArray(x);
x[0][0].foo = 2;

// now see what the value is in `y`
// if it 2, then it been changed and is not a true copy
// both arrays have a reference to the same object
console.log(y[0][0].foo);    // logs 2

      



The same result will happen if the third level is also different. You would have to recursively traverse each element that is the type of an object and then clone that object to get a complete clone of everything in the nested arrays.

If you want code that will make a deep copy (to an arbitrary level) and work for all data types, see here .

FYI, your function cloneArray()

assumes that all the first level members of your array are arrays themselves and therefore do not work if they contain any other value type.

+2


source


Your code doesn't work :

  • If your array contains other variables like numbers or strings (not just arrays), it will fail because it will call arr[i].slice()

    , but since this arr[i]

    is a number, it doesn't .slice

    , and this will throw an error.
  • Your function will keep all references to objects and other materials inside the array alive anyway. This way you won't actually get a copy of your array.

Example:

var a = [1,2, [11,13], ['ok']];
var b = cloneArray(a);

> TypeError: undefined is not a function // because numbers have no .slice method

      



Decision:

To copy the array, you will need to make a deep copy. Since creating a deep copy would require a function that uses recursion to deep copy any object or array within the main one, the easiest way to do this is to use jQuery and its method .extend

, which does a deep copy of the array, see here for more information.

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

b[0][0] = 99;
a[0][0] // still 1

      

+2


source


Array.prototype.slice is not suitable for cloning arrays

This should work well for you

function deepClone(arr) {
  var len = arr.length;
  var newArr = new Array(len);
  for (var i=0; i<len; i++) {
    if (Array.isArray(arr[i])) {
      newArr[i] = deepClone(arr[i]);
    }
    else {
      newArr[i] = arr[i];
    }
  }
  return newArr;
}

      

If you need to support an older browser be sure to use a polyfill (via MDN)

if(!Array.isArray) {
  Array.isArray = function(arg) {
    return Object.prototype.toString.call(arg) === '[object Array]';
  };
}

      

+2


source


Here's my recursive approach to "cloning" a multidimensional array. It goes all the way down to the deepest levels:

if ( !Array.clone )
{
        Array.prototype.clone = function()
        {
                var _arr = ( arguments[0] == null ) ? [] : arguments[0] ; 
                for( var _p = 0 ; _p < this.length ; _p++ )
                {
                         if ( this[_p] instanceof Array )
                         {
                                 var _sub = [] ;
                                 this[_p].clone( _sub ) ;
                                 _arr.push( _sub.slice() );
                         }
                         else _arr.push( this[_p] );
                }

                return _arr ;
        }
}

      

Now try this code:

var _a = [ "a", "b", [ "c", "d", [ "e", "f" ] ] ];
var _b = _a.clone();
console.log( _b );

      

Vars _a and _b are two different objects: if you remove an element from var _b, then var _a will not be affected.

0


source







All Articles