4

Trying to understand JavaScript and writing to objects. I have an object here:

  {
   "name":"",
   "children":[
      {
         "name":"Level 1",
         "children":[
            {
               "name":"Level 2",
               "children":[
                  {
                     "name":"Level 3",
                     "children":[
                        {
                           "name":"Level 4",
                           "children":[
                              {
                                 "name":"Speed",
                                 "children":null,
                                 "id":6
                              }
                           ],
                           "id":5
                        }
                     ],
                     "id":4
                  }
               ],
               "id":3
            }
         ],
         "id":2
      },
      {
         "name":"Level 1",
         "children":[
            {
               "name":"Level 2",
               "children":[
                  {
                     "name":"Level 3",
                     "children":[
                        {
                           "name":"Level 4",
                           "children":[
                              {
                                 "name":"Cost",
                                 "children":null,
                                 "id":11
                              }
                           ],
                           "id":10
                        }
                     ],
                     "id":9
                  }
               ],
               "id":8
            }
         ],
         "id":7
      },
      {
         "name":"Level 1",
         "children":[
            {
               "name":"Level 2",
               "children":[
                  {
                     "name":"Level 3",
                     "children":[
                        {
                           "name":"Level 4",
                           "children":[
                              {
                                 "name":"Manufacturability",
                                 "children":null,
                                 "id":16
                              }
                           ],
                           "id":15
                        }
                     ],
                     "id":14
                  }
               ],
               "id":13
            }
         ],
         "id":12
      }
   ],
   "id":1
}

and I'm trying to understand how to search for a given id value and change its name value.

In my case, I know that I can access values using d.id and d.name using the code below (this is part of a widget display; the name values populate it)

var jstring = this.model.get('value') ? this.model.get('value') : "{}";
// where 'value' = demo.json

var root = JSON.parse(jstring)
var g = this.g = svg.selectAll("g")
     .data(partition.nodes(root))
     .enter().append("g");
var path = this.path = g.append("path")
      .attr("d", arc)
      .style("fill", function(d) {
      d.active = d.active ? true : false
      return d.active || d.center ? color[1] : color[0];
                })
      .on("dblclick",dblclick);
var text =  this.text = g.append("text")
                .attr("transform", function(d) { return "rotate(" + computeTextRotation(d) + ")"; })
                .attr("x", function(d) { return y(d.y); })
                .attr("dx", "6") // margin
                .attr("dy", ".35em") // vertical-align
                .text(function(d) { return d.name; });

For example, if I click on a certain area on the widget, I can populate an input box by setting its value to d.name and it gives me the correct value.

function dblclick(d)
           {


                    var input = document.getElementById("name");
                    input.value = d.name;


                    $( "#dialog" ).dialog(
                    {

                    buttons: {
                        Save: function() {
                        d.name = input.value;

                   var newString = JSON.stringify(root, function(key, val) {

                      if (Array.isArray(val)){
                          return val
                      }
                      if (val != null && typeof val == "object") {
                          val = _.pick(val, 'name', 'children', 'id');
                          if(d.id == val.id){

                              input.value = d.name;
                             console.log(d.name)
                          }

                          val.children = Array.isArray(val.children) ? val.children : [];

                          return val
                      }
                      return val
                    })
                  self.model.set('value', newString)
                  self.update()
                  console.log(newString)

I found a similar question here but I don't understand how to apply the answer to modify my JSON.

Also here is a fiddle of what I've tried: http://jsfiddle.net/CVvW4/237/ . I followed an answer from another question but my implementation is wrong.

5
  • What exactly are you trying to achieve? Where is your click event handler code? Can you be more specific? Commented May 13, 2016 at 16:45
  • Are you searching a certain id value in the data tree? If so, you will need to call your search function recursively. Commented May 13, 2016 at 16:46
  • @iulian I left it out because I thought it'd be too much code but I'll edit to add it Commented May 13, 2016 at 16:48
  • @iulian edit is up.. I want to be able to click on the widget and have a dialog display with the value that was clicked (it does this currently). I want a user to be able to change this value and save it to the JSON to be updated on the widget. Commented May 13, 2016 at 16:55
  • @C14L I don't have much experience so can you give me an example on how to do this? Commented May 13, 2016 at 16:56

2 Answers 2

3
  • Your jsonStr is already a json object, no need to stringify and parse it
  • You have a nested structure, to find something you will need a recursive function

Here is how to find a node given its id:

var root = jsonStr

function findById(node, id) {
  if (node.id == id) return node; // we found the node with the id given, return it
  var result = null; // if the id wasn´t the one we were looking, we need to look if it is in its children
  if (node.children) {
    for (var i = 0; i < node.children.length && result == null; i++) {
        result = findById(node.children[i], id)
    }
  }
  return result; // return null if it wasn´t in its children, return the node if it was
}

console.log(findById(root, 16))

Now, to change its name you can simply do:

findById(root, 16).name = 'asd';
Sign up to request clarification or add additional context in comments.

10 Comments

thanks for the detailed explanation, it makes more sense now. I added your function to my click event and in the console I can see the correct value change for newString and root but the jstring variable doesn't change. Could you take a look? jsfiddle.net/baqxh3sf
@c2bh2016 try using self.model.get('value') instead of console.log(jstring)
I just tried, there was no change to the widget. any other suggestion?
please add a short circuit for the for loop if a node is found.
@NinaScholz added to stop when found
|
0

I really like the accepted answer provided by @juvian which I up-voted.

I provide this one to show how you can name the child array and the property we wish to match on for the node. I also protect the array iteration by type.

I provide here some details regarding JSON, JavaScript Objects and when to parse, when not to parse by providing some examples of each.

Note that I added a small function typeName to assist in discovery of names and thus we do not attempt to iterate a non-array type (null one, string etc.) by the same name as the property we are searching for.

NOTE: I did NOT protect against the type matching of the property against the searchFor value but if that was important, string "1" vs number 1 you could put in an enhancement using the typeName as well.

Example to play with: https://jsfiddle.net/MarkSchultheiss/s4sxy4f6/

HERE is a stripped down version where I check for success prior to trying to assign the name to it: https://jsfiddle.net/MarkSchultheiss/s4sxy4f6/1/

Code and objects to show types:

// this is just a string, but special as it is a JSON string and can be parsed
var myJSON = '{"children":[{"children":[{"children":[{"children":[{"children":[{"children":null,"id":6,"name":"Speed"}],"id":5,"name":"Level 4"}],"id":4,"name":"Level 3"}],"id":3,"name":"Level 2"}],"id":2,"name":"Level 1"},{"children":[{"children":[{"children":[{"children":[{"children":null,"id":11,"name":"Cost"}],"id":10,"name":"Level 4"}],"id":9,"name":"Level 3"}],"id":8,"name":"Level 2"}],"id":7,"name":"Level 1"},{"children":[{"children":[{"children":[{"children":[{"children":null,"id":16,"name":"Manufacturability"}],"id":15,"name":"Level 4"}],"id":14,"name":"Level 3"}],"id":13,"name":"Level 2"}],"id":12,"name":"Level 1"}],"_default":{},"id":1,"name":""}';

// This is a JavaScript Object
var myObject = {
  "children": [{
    "children": [{
      "children": [{
        "children": [{
          "children": [{
            "children": null,
            "id": 6,
            "name": "Speed"
          }],
          "id": 5,
          "name": "Level 4"
        }],
        "id": 4,
        "name": "Level 3"
      }],
      "id": 3,
      "name": "Level 2"
    }],
    "id": 2,
    "name": "Level 1"
  }, {
    "children": [{
      "children": [{
        "children": [{
          "children": [{
            "children": null,
            "id": 11,
            "name": "Cost"
          }],
          "id": 10,
          "name": "Level 4"
        }],
        "id": 9,
        "name": "Level 3"
      }],
      "id": 8,
      "name": "Level 2"
    }],
    "id": 7,
    "name": "Level 1"
  }, {
    "children": [{
      "children": [{
        "children": [{
          "children": [{
            "children": null,
            "id": 16,
            "name": "Manufacturability"
          }],
          "id": 15,
          "name": "Level 4"
        }],
        "id": 14,
        "name": "Level 3"
      }],
      "id": 13,
      "name": "Level 2"
    }],
    "id": 12,
    "name": "Level 1"
  }],
  "_default": {},
  "id": 1,
  "name": ""
};

// just to get the name of the objects type from the object prototype
function typeName(obj) {
  // splits and returns second part of string such as "[object Array]" returns the "Array" removing the closing bracket
  return Object.prototype.toString.call(obj).match(/.* (.*)\]/)[1];
}
// show some type names to assist with object "type" education
console.log("myJSON:" + typeName(myJSON)); // String
console.log("myObject:" + typeName(myObject)); // Object
console.log("Children of object:" + typeName(myObject.children)); // Array

console.log("Children Type:" + typeof myObject["children"] + " typeName:" + typeName(myObject.children));
console.log(Object.keys(myObject)); // thus we can get the string "children" from the object with Object.keys(myObject)[0]

var root = JSON.stringify(myObject); // create string of object
console.log("root:" + typeName(root)); // String

var newObject = JSON.parse(myJSON); // create new object of string

// create function with private name to call internally
// done this way to allow for external modification of the name without need to change the code inside it.
var findByProperty = function findNext(node, searchValue, propertyName, childName) {
  if (node.hasOwnProperty(propertyName) && node[propertyName] == searchValue) return node; // node found return it
  var result = null;
  // has child array by the name and it is non-empty array
  if (node.hasOwnProperty(childName) && typeName(node[childName]) === 'Array' && node[childName].length) {
    for (var i = 0; i < node[childName].length && result == null; i++) {
      result = findNext(node[childName][i], searchValue, propertyName, childName);
    }
  }
  return result; // return null if not in children, return the node if it was
}
var searchFor = 16;
console.log('searchFor is a type of:'+typeName(searchFor));
var propertyName = "id";
var childrenArrayName = "children";

// show how we can return the found node then modify it
var found = findByProperty(myObject, searchFor, propertyName, childrenArrayName);
found.name = 'Freddy';
console.log(myObject);
console.log(myObject["children"][2]["children"][0]["children"][0]["children"][0]["children"][0].name); // logs "Freddy"

var secondfound = findByProperty(newObject, searchFor, propertyName, childrenArrayName);
secondfound.name = 'Walter';// modify the object via the node
console.log(newObject);
console.log(newObject["children"][2]["children"][0]["children"][0]["children"][0]["children"][0].name); // logs "Walter"
//  just to show that the actual object is the one found
console.log(secondfound.name === newObject["children"][2]["children"][0]["children"][0]["children"][0]["children"][0].name); // logs true

Here is the output of the console logs:

myJSON:String 
VM78:125 myObject:Object  
VM78:126 Children of object:Array  
VM78:128 Children Type:object typeName:Array  
VM78:129 ["children", "_default", "id", "name"]  
VM78:132 root:String  
VM205:148 searchFor is a type of:Number  
VM281:153 Object {children: Array[3], _default: Object, id: 1, name: ""} 
VM281:154 Freddy  
VM337:158 Object {children: Array[3], _default: Object, id: 1, name: ""} 
VM337:159 Walter  
VM344:160 true

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.