6

In my application I create a JavaScript object based on a JSON response from the server similar to this:

{
  name: "root",
  id: 1,
  children: [
    {
      name: "child one",
      id: 11,
      children: [
       {name: "grand child 1", id: 111, children: []},
       {name: "grand child 2", id: 112, children: []}
      ]
   },
   {
     name: "child two",
     id: 12,
     children: []
   }
  ]
}

I create a new node such as:

 {name: "grandchild three", id: 113, children:[]}

With this in mind, how can I add this new grandchild to its parent with id 11? Please note that I don’t know the static path to node with id == 11 so I am wondering how I could obtain that node with just knowing it's id.

Edit: please note the id's in the real case do NOT encode the path to objects. I created this simple example for demonstration of the data structure I am dealing with. But I can not retrieve the path to the object using its id in my real application.

Aras
  • 5,878
  • 9
  • 49
  • 76
  • 2
    `object.children[0].children.push(newobject)` Where object is your original object and newobject is your new node. – Asad Saeeduddin Oct 15 '12 at 16:10
  • thanks for the quick resonse. But how can I find the path to the object if I only have the *parent id*? – Aras Oct 15 '12 at 16:13

5 Answers5

9

See this fiddle: http://jsfiddle.net/2Dvws/

It will find an object by ID. And push the new child. Since every object within Javascript is a reference, you can return it as an var.

var ob = {
    name: "root",
    id: 1,
    children: [
        {
        name: "child one",
        id: 11,
        children: [
            {
            name: "grand child 1",
            id: 111,
            children: []},
        {
            name: "grand child 2",
            id: 112,
            children: []}
        ]},
    {
        name: "child two",
        id: 12,
        children: []}
    ]
};

The function which will return the found element. Will look into all child elements.

function findObjectById(root, id) {
    if (root.children) {
        for (var k in root.children) {
            if (root.children[k].id == id) {
                return root.children[k];
            }
            else if (root.children.length) {
                return findObjectById(root.children[k], id);
            }
        }
    }
};

var bla = findObjectById(ob, 111);

console.log(bla);
bla.children.push({
        name: "child x",
        id: 1111,
        children: []
});
console.log(ob);

Output is that child with id 111 will have 1 child with id 1111 ​

Niels
  • 48,601
  • 4
  • 62
  • 81
  • Thank you! this is the only answer that considers my nested tree structure. – Aras Oct 15 '12 at 17:40
  • You do have to watch, the function can also return `null` and then you can't do `bla.children.push` since `bla` is `NULL`. – Niels Oct 15 '12 at 17:45
2

I assume the id consists of the parent-id plus the index (1 to 9) in the children array? Then you can go like that:

var rootobj = {…};
var newnode = {name: "grandchild three", id: 113, children:[]};

var id = ""+newnode.id;
var cur = [rootobj];
for (var i=0; i<id.length-i; i++)
    cur = cur[id.charAt(i)-1].children;
cur[id.charAt(i)-1] = newnode;
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • darn it, you are too clever! No I cant assume the id's are in that form. I just made this simple example up to help discuss the problem. The id's in the real API I am working with are generated independent of the path to the object. – Aras Oct 15 '12 at 16:29
  • OK, so how else could you know the path to a node? Or don't you know at all, and need to do a [full tree search](http://en.wikipedia.org/wiki/Tree_traversal) for the parent node whose `id` you know? That shouldn't be too difficult. – Bergi Oct 15 '12 at 16:51
2

Niels' answer is a good start, but doesn't fully traverse the tree (e.g. it will break if the node you're looking for is the child of the second child of the root). Also it breaks if the root is the id you're looking for. Here are my refinements:

  function findObjectByID(root, id) {
    if (root.name == id){
      return root;
    }
    if (root.children) {
      for (var k in root.children) {
        if (root.children[k].name == id) {
          return root.children[k];
        }
        else if (root.children[k].children) {
          result = findObjectByID(root.children[k], id);
          if (result) {
            return result;
          }
        }
      }
    }
  };
seaotternerd
  • 6,298
  • 2
  • 47
  • 58
0

How about this.

for(var a = 0; a < object.length; a++) {
    for(var b = 0; b < obj.children.length; b++) {
        if(object[a].children[b].id == 11) {
           object[a].children[b].children.push({
                name: "grandchild three", 
                id: 113, 
                children: []
            });
        }
    }
}
Austin Brunkhorst
  • 20,704
  • 6
  • 47
  • 61
  • Does this traverse though the tree? it looks like it only searches children of one node. The problem is I don’t know how many level deep my node it, I just have its id. – Aras Oct 15 '12 at 16:16
  • Updated it to support traversing the tree. – Austin Brunkhorst Oct 15 '12 at 16:21
  • Ok, I get the idea at this point. So I need to do a nested search on the tree, find the node I am going to insert into and then add to its children. But for the sake of having a complete and correct answer, I think you should update your answer to use a recursive or another form of tree search algorithm. The current form assumes that my data structure is only two level deep. That can not be assumed with a nested tree that can grow at any leaf. Thanks again! – Aras Oct 15 '12 at 16:26
  • 3
    @Austin: This does not really traverse the tree, but only loops through the children of the root node. – Bergi Oct 15 '12 at 16:26
  • No problem. Next time it will help to be more specific in your question and include that. @Bergi I realize that after commenting. I meant looping through children nodes. – Austin Brunkhorst Oct 15 '12 at 16:27
0

Here is a solution using object-scan. Using a library should make this a bit more readable and maintainable.

// const objectScan = require('object-scan');

const insert = (haystack, parentId, node) => objectScan(['**.id'], {
  abort: true,
  rtn: 'bool',
  filterFn: ({ value, parent }) => {
    if (value === parentId) {
      parent.children.push(node);
      return true;
    }
    return false;
  }
})(haystack);

const obj = { name: 'root', id: 1, children: [ { name: 'child one', id: 11, children: [ { name: 'grand child 1', id: 111, children: [] }, { name: 'grand child 2', id: 112, children: [] } ] }, { name: 'child two', id: 12, children: [] } ] };

console.log(insert(obj, 11, { name: "grandchild three", id: 113, children: [] })); // true iff inserted
// => true

console.log(obj);
// => { name: 'root', id: 1, children: [ { name: 'child one', id: 11, children: [ { name: 'grand child 1', id: 111, children: [] }, { name: 'grand child 2', id: 112, children: [] }, { name: 'grandchild three', id: 113, children: [] } ] }, { name: 'child two', id: 12, children: [] } ] }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan@13.8.0"></script>

Disclaimer: I'm the author of object-scan

vincent
  • 1,953
  • 3
  • 18
  • 24