views:

191

answers:

5

The following in a Javascript console:

var a = {'foo': []};
var b = {};

for (var key in a) {
   b[key] = a[key];
}

a['foo'].push(1);

console.log(b);

Yields:

Object foo=[1]

I want to make a copy by value in b of each array for each key in a. Is there an easier way?

A: 

Easier way:

var a = {'foo': []};
var b = a;

a['foo'].push(1);

console.log(b);

Output is the same.

Edit:

var a = {'foo': []};
var b = {};

for (var key in a) {
    if (a.hasOwnProperty(key)) {
        b[key] = [];
        for (var i = 0; i < a[key].length; i += 1) {
            b[key][i] = a[key][i];
        }
    }
}

a['foo'].push(1);

console.log(b);
Anatoliy
Anatoliy: I don't want b to simply be a reference to a; I want b to be a unique copy by value of the arrays stored in a so that when I push to a's arrays it does not affect b.
Wells
Fixed. Currently output is object with empty array.
Anatoliy
You can simplify all of that deep-copy stuff with `a[key].slice(0)`.
Eli Grey
+4  A: 

You could make a "clone" function that creates a new object, based on the original object constructor, and then clone that original object properties also if they are objects:

function clone(obj){
  if(typeof(obj) != 'object' && obj != null) 
    return obj; // return the value itself if isn't an object
                // or null, since typeof  null == 'object';

    var temp = new obj.constructor();

    for(var key in obj)
        temp[key] = clone(obj[key]);
    return temp;
}


var a = {'foo': []};
var b = clone(a);

a['foo'].push(1);

console.log(b); // Object foo=[0]
CMS
I ran across this problem before, and this is exactly how I solved it. +1
NateDSaint
Prototyping breaks this. Simply `Object.prototype.foo = function () {};` will create infinite recursion.
Jonathan Lonowski
@Jonathan: added an if statement to stop recursion.
CMS
Does this work? `a['bar'] = a; console.log(clone(a));`
Josh Stodola
Same problem here CMS: `obj.constructor()` is going to die on constructor functions (`temp` will be `undefined`), and potentially clobber globals.
Crescent Fresh
@Crescent: Exactly, the same problem!, added the `new` operator while I think on something better...
CMS
A: 

I think it depends on your library. If you're using Prototype.js, then you can just call toJSON() on the object, and then evalJSON() the result.

//ex:
var foo = {"foo":12, "bar":[ 1, 2, 3 ] };
var bar = foo.toJSON().evalJSON();

alert( bar.bar ); // 1, 2, 3

If you do decide to use a custom function to accomplish this, I would recommend that it has allowances for Arrays, which you can clone using splice(), and other values which don't pass by reference natively (say, Booleans). These may complicate your functions a little, but they turn string look-ups to boolean evaluations. This is a case where more is faster.

Christopher W. Allen-Poole
A: 

As this will add support for deep-copying arrays in your code:

var a = {'foo': []};
var b = {};

for (var key in a) {
   if (Object.prototype.toString.call(b[key]) === "[object Array]") {
      b[key] = a[key].slice(0);
   } else {
      b[key] = a[key];
   }
}

a['foo'].push(1);

console.log(b);
Eli Grey