c2bh2016
c2bh2016

Reputation: 55

How to search JavaScript object and change value

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.

Upvotes: 4

Views: 963

Answers (2)

Mark Schultheiss
Mark Schultheiss

Reputation: 34217

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

Upvotes: 0

juvian
juvian

Reputation: 16068

  • 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';

Upvotes: 3

Related Questions