Reputation: 55
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
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
Reputation: 16068
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