0

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?

olyar
  • 64
  • 10
  • because you are setting all .kids properties to the same `child` array. Objects and arrays are passed by reference not value in JS. Also you are probably wanting to create an object and not an array for each new partent/child i.e. `parent[b] = {};` instead of `parent[b] = [];` – Patrick Evans Nov 30 '14 at 15:46
  • Because of `parent[b].kids = child;`. So all `kinds` are the same object reference to `child` object. – dfsq Nov 30 '14 at 15:46

2 Answers2

1

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.

drinchev
  • 19,201
  • 4
  • 67
  • 93
  • Note, that since OP is using array as an object (setting `.name` property), `slice` will not help in this case. – dfsq Nov 30 '14 at 15:52
1

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.

Community
  • 1
  • 1
hgazibara
  • 1,832
  • 1
  • 19
  • 22