olyar
olyar

Reputation: 64

Javascript set array as attribute of another array element

I want to make an array of parent and another array of children, and later set an array of children to each parent.

Here is the code:

var parent = [];
var child = [];

// Set kids
for (a = 0; a <= 5; a++) {
    child[a] = [];
    child[a].name = "kid " + a;
}

// Set parents and their kids
for (b = 0; b < 2; b++) {
    parent[b] = [];
    parent[b].name = "parent " + b;
    parent[b].kids = child;
}

// One parent has kid name anna
parent[0].kids[2].name = "anna";

// Output
for (a = 0; a < 2; a++) {
    console.log("");

    for (b = 0; b < 5; b++) {
        console.log(b + " -> " + parent[a].name + " " + parent[a].kids[b].name);
    }
}

First parent

0 -> parent 0 kid 0

1 -> parent 0 kid 1

2 -> parent 0 anna

3 -> parent 0 kid 3

4 -> parent 0 kid 4


Second parent

0 -> parent 1 kid 0

1 -> parent 1 kid 1

2 -> parent 1 anna

3 -> parent 1 kid 3

4 -> parent 1 kid 4


Why do both parent have the same children only first one should have the kid name anna, and more important How can I make it to work properly?

Upvotes: 0

Views: 1951

Answers (2)

Ivan Drinchev
Ivan Drinchev

Reputation: 19591

Because you are passing a reference to your array in your newly created object.

var a = [1,2,3,4];
var b = a;
b[0] = 2;
a[0] === 2; // true

In order to send a new array ( cloned ). You can do :

for (b = 0; b < 2; b++) {
    parent[b] = [];
    parent[b].name = "parent " + b;
    parent[b].kids = child.slice(); // Slice creates a new array from the old
}

EDIT 1

As @dfsq has stated in the original question and code provided, you are actually using an object not an array. Without going deep into the discussion of what is the best way to clone an object, let's assume you are using jQuery in your code, in which case you can safely do :

parent[b].kids = $.extend({}, child);

More about working with objects you can read in Mozilla Developer Network.

Upvotes: 1

hgazibara
hgazibara

Reputation: 1832

As already mentioned, your problem is assigning the same array of children to every parent.

If array child contained primitive values, using any way of copying an array and then setting the kids property would actually help, because shallow copy would be sufficient. However, the problem presented here is a little bit more complicated because elements are not primitives, so a method for deep copying should be used.

To be more specific, it's necessary not only to copy a whole array, but to also separately make a copy of every single item.

You can try to use something like this copy children every time you assign it to parent. This isn't the most beautiful of fastest method out there, but should be good enough to start.

function clone(obj) {
    if(obj == null || typeof(obj) != 'object')
        return obj;

    var temp = obj.constructor(); // changed

    for(var key in obj) {
        if(obj.hasOwnProperty(key)) {
            temp[key] = clone(obj[key]);
        }
    }
    return temp;
}

function copy_kids(kids) {
    var new_kids = [];
    for (var kid_id = 0; kid_id < kids.length; ++kid_id) {
        new_kids[kid_id] = clone(kids[kid_id]);
    }
    return new_kids;
}

The function for copy an object was taken from an older answer.

To make your code work, change the following:

// Older
parent[b].kids = child;

// Newer
parent[b].kids = copy_kids(child);

To be completely precise, this isn't really deep copying, but just a little bit deeper than copying the array itself. You can read more about deep vs shallow copy here.

Upvotes: 1

Related Questions