I have some code that will return me the name of the subcat clicked.
Here is the data below:
theData = {
"categories": [
{
"id": "661",
"name": "some name",
"description": "some description",
"subcat": [
{
"id": "662",
"name": "sub 1",
"translations": null
},
{
"id": "663",
"name": "sub 2",
"translations": null
}
],
"image": null
},
{
"id": "657",
"name": "some other name",
"description": "",
"subcat": [
{
"id": "456",
"name": "sub 12",
"translations": null
},
{
"id": "656",
"name": "sub 15",
"translations": null
}
],
"image": null
}
]
};
I need some way to find the parent id of the subcat name.
For example if I gave it "sub 15", it would return "661" which is the parent id
How can I do this?
There's no way by default to access the "parent" of an object in Javascript -- it could be referenced in any number of other objects, or even by itself, so it's not possible to determine what the sole parent of an object is.
Instead, we'll just iterate through all the data until we find the matching id, and return null if we never find it. This solution counts on your IDs being unique entities, so if that's not the case, it'll have to be changed. With that said, here's an example:
function getParent(subID) {
for (var i in theData.categories) {
var parent = theData.categories[i];
if ('subcat' in parent && 'length' in parent.subcat) {
for (var j = 0; j < parent.subcat.length; j++) {
if (parent.subcat[j].id === subID) {
return parent.id;
}
}
}
}
return null;
}
If you don't like your function returning null, you can always alter it so that it returns -1, assuming that -1 is out-of-band for your IDs. Note also that this is hardly an optimal solution, so if you're intending to use this for large amounts of data, you'll want to look into faster and/or more efficient search algorithms.
try this .we have to iterate through all the data
[fiddle][1]
Fiddle
var input = "sub 15";
var id =-1;
for(var i=0;i<theData.categories.length;i++){
if(theData.categories[i].subcat!=null){
for(var j=0;j<theData.categories[i].subcat.length;j++){
if(theData.categories[i].subcat[j].name==input){
id= theData.categories[i].id;
break;
}
}
}
}
console.log(id)
You might consider more a generic solution, which searches for a given key and a value in the subcat array. Then take only the first object's id.
function getParentId(key, value) {
return (theData.categories.filter(function (a) {
return a.subcat.some(function (b) {
return b[key] === value;
});
})[0] || {}).id;
}
var theData = { categories: [{ id: 661, name: "some name", description: "some description", subcat: [{ id: 662, name: "sub 1", translations: null }, { id: 663, name: "sub 2", translations: null }], image: null }, { id: 657, name: "some other name", description: 0, subcat: [{ id: 456, name: "sub 12", translations: null }, { id: 656, name: "sub 15", translations: null }], image: null }] };
console.log(getParentId('name', 'sub 15'));
This may not be the best way but hope this will be useful
var getSubCat = []; // create an empty array to map parent id with sub name
var getCat = theData.categories; // get categories
getCat.forEach(function(item) { // loop through categories
item.subcat.forEach(function(elem) {
getSubCat.push({
id: item.id, // this is parent id
sub: elem.name // name inside each subcat object. Total 4 subcat object
});
})
})
var parentId = getSubCat.filter(function(elem){ // filter the getSubCat array
return elem.sub ==='sub 15'
})[0].id // parentId is an array, we need first object so [0]
alert(parentId)
Now I am pushing name inside each of subcat object. On requirement you can also push their id
DEMO
The below solution looks similar to furkle's solution. But here it uses jquery to iterate through the json objects.
function getParent(subID) {
var parentId = null;
$.each(theData.categories, function(i, v)
{
$.each(v.subcat, function(idx, obj){
if(obj.name ==subID)
parentId = v.id
})
})
return parentId;
}
Related
I have an object which contains questions with different types multiple, single and text.
const data = {
questions: [
{
"question": "Question 1",
"value": [
"value_1.1.1",
"value_1.1.2"
],
"type": "multiple",
"options": [
{
"value": "value_1.1.1",
"label": "Value 1.1.1"
},
{
"textValue":"Additional text value",
"value": "value_1.1.2",
"label": "Value 1.1.2"
},
{
"value":"value_1.1.3",
"label":"Value 1.1.3",
}
]
},
{
"question": "Question 2",
"value": "value_2.1.1",
"type": "single",
"options": [
{
"value": "value_2.1.1",
"label": "Value 2.1.1"
},
{
"value": "value_2.1.2",
"label": "Value 2.1.2"
},
]
},
{
"question":"Question 3",
"textValue":"Test 12345",
"type":"text"
}
]
}
I want to create a new array with objects, which contains the question key and the value key.
The value key must contain the label of the selected options.
For a multiple type, the value can contain multiple labels.
The selected option can be retrieved from the parent value key, if it is equal to the value of the option, I want to use the label of this option as value.
In some cases an option also contains a textValue (see 1.1.3), then this must also be added to the value.
So the new array should look like this for the example above:
const newData = [
{
question: "Question 1",
value: ['Value 1.1.1', 'Value 1.1.2', 'Additional text value']
},
{
question: "Question 2",
value: ['Value 2.1.1']
},
{
question: "Question 3",
value: ['Test 12345']
}
];
Well basically loop over the questions, handling each by its own type. The tricky part is the type: multiple but it is managable.
Update: Fixed according to comment
const data = {questions:[{question:"Question 1",value:["value_1.1.1","value_1.1.2"],type:"multiple",options:[{value:"value_1.1.1",label:"Value 1.1.1"},{value:"value_1.1.2",label:"Value 1.1.2"},{textValue:"test12",value:"value_1.1.3",label:"Value 1.1.3"}]},{question:"Question 2",value:"value_2.1.1",type:"single",options:[{value:"value_2.1.1",label:"Value 2.1.1"},{value:"value_2.1.2",label:"Value 2.1.2"},]},{question:"Question 3",textValue:"Test 12345",type:"text"}]};
function get_option_label(options, value) {
return options.find(item => item.value == value).label || value
}
const newData = [];
data.questions.forEach(function(obj) {
var new_obj = {
question: obj.question
}
if (obj.type == "multiple") {
new_obj.value = JSON.parse(JSON.stringify(obj.value))
new_obj.value = new_obj.value.map(item => get_option_label(obj.options, item))
// add textValue for multiple question
// but only if its value is on parent
var objText = obj.options.find((item) => item.textValue);
if (objText && obj.value.indexOf(objText.value) >= 0) {
new_obj.value.push(objText.textValue)
}
}
if (obj.type == "single") {
new_obj.value = [get_option_label(obj.options, obj.value)]
}
if (obj.type == "text") {
new_obj.value = [obj.textValue]
}
newData.push(new_obj)
})
// now replacing all values with labels
console.log(newData);
.as-console-wrapper {
max-height: 100% !important;
top: 0
}
Restructuring array of objects to new array
Problem
There’s an array of objects that contains plain strings and might contain nested arrays as well. We want to create a new Array that will contain a node for each item in the array and separate nodes for each array item connected to its parent. Each parent node should have the following structure:
{
id: uuidv4(),
position: { x: 0, y: 0 },
data: { label: <item data goes here> }
}
Each array node with the following schema above, should also have a connection edge item added to the array with the following properties:
{
id: ‘e<array item Id>-<parentId>’,
source: <array item Id>,
target: <parentId>,
}
Example
We have the following array of objects for example:
[
{
"author": "John Doe",
"age": 26,
"books": [
{
"title": "Book 1"
},
{
"title": "Book 2",
"chapters": [
{
"title": "No Way Home",
"page": 256
}
]
}
]
}
]
The expected output is:
[
{
"id": "1",
"data": {
"label": {
"author": "John Doe",
"age": 26,
}
}
},
{
"id": "2",
"data": {
"label": "books" // key of array
}
},
{
"id": "3",
"data": {
"label": {
"title": "Book 1"
}
}
},
{
"id": "4",
"data": {
"label": {
"title": "Book 2"
}
}
},
{
"id": "5",
"data": {
"label": "chapters" // key of array
}
},
{
"id": "6",
"data": {
"label": {
"title": "No Way Home",
"page": 256
}
}
},
{
"id": "e2-1",
"source": "2",
"target": "1"
},
{
"id": "e3-2",
"source": "3",
"target": "2"
},
{
"id": "e4-2",
"source": "4",
"target": "2"
},
{
"id": "e5-4",
"source": "5",
"target": "4"
},
{
"id": "e6-5",
"source": "6",
"target": "5"
}
]
First of all, I would not be answering if there was not already a good answer. Please, on StackOverflow, always show your own attempts and explain where you got stuck. But since there is already an answer, I think this version might be a bit simpler.
Second, I'm assuming this output format is some sort of directed graph, that the first half is your list of vertices and the second half a list of edges. If so I don't know if your output format is constrained here. But if you had the option, I would think a better structure would be an object with vertices and edges properties, each containing an array. You might then not need the edges' ids. And the code could also be simplified.
This version first converts to an intermediate structure like this:
[
{id: "1", data: {label: {author: "John Doe", age: 26}}, children: [
{id: "2", data: {label: "books"}, children: [
{id: "3", data: {label: {title: "Book 1"}}, children: []},
{id: "4", data: {label: {title: "Book 2"}}, children: [
{id: "5", data: {label: "chapters"}, children: [
{id: "6", data: {label: {title: "No Way Home"}}, children: []}
]}
]}
]}
]}
]
Then we flatten that structure into the first section of the output and use it to calculate the relationships (edges?) between nested nodes to go in the second section.
The code looks like this:
const transform = (input) => {
const extract = (os, nextId = ((id) => () => String (++ id)) (0)) => os .map ((o) => ({
id: nextId(),
data: {label: Object .fromEntries (Object .entries (o) .filter (([k, v]) => !Array .isArray (v)))},
children: Object .entries (o) .filter (([k, v]) => Array .isArray (v)) .flatMap (([k, v]) => [
{id: nextId(), data: {label: k}, children: extract (v, nextId)},
])
}))
const relationships = (xs) =>
xs .flatMap (({id: target, children = []}) => [
... children .map (({id: source}) => ({id: `e${source}-${target}`, source, target})),
... relationships (children),
])
const flatten = (xs) =>
xs .flatMap (({children, ...rest}) => [rest, ... flatten (children)])
const res = extract (input)
return [...flatten (res), ... relationships (res)]
}
const input = [{author: "John Doe", age : 26, books: [{title: "Book 1"}, {title: "Book 2", chapters: [{title: "No Way Home", page: 256}]}]}]
console .log (transform (input))
.as-console-wrapper {max-height: 100% !important; top: 0}
We use three separate recursive functions. One does the recursive extract into that intermediate format. Along the way, it adds id nodes using a nextId stateful function (something I usually avoid, but seems to simplify things here.) Then flatten simply recursively lifts the children to sit alongside their parents. And relationships (again recursively) uses the ids of the parent- and child-nodes to add an edge node.
Using these three separate recursive calls is probably less efficient than some other solutions, but I think it leads to much cleaner code.
One has to choose a self recursive approach which in a generic way can process both, array-items and object-entries. Also, while the recursive process takes place, one not only has to create and collect the consecutively/serially numbered (the incremented id value) data nodes, but one in addition needs to keep track of every data node's parent reference in order to finally concatenate the list of edge items (as the OP calls it) to the list of data nodes.
function flattenStructureRecursively(source = [], result = [], tracker = {}) {
let {
parent = null, edgeItems = [],
getId = (id => (() => ++id))(0),
} = tracker;
const createEdgeItem = (id, pid) => ({
id: `e${ id }-${ pid }`,
source: id,
target: pid,
});
const putNodeData = node => {
result.push(node);
if (parent !== null) {
edgeItems.push(createEdgeItem(node.id, parent.id));
}
// every data node is a parent entity too.
parent = node;
};
if (Array.isArray(source)) {
result.push(
...source.flatMap(item =>
flattenStructureRecursively(item, [], {
getId, parent, edgeItems,
})
)
);
} else {
let {
dataNode,
childEntries,
} = Object
.entries(source)
.reduce(({ dataNode, childEntries }, [key, value]) => {
if (value && (Array.isArray(value) || (typeof value === 'object'))) {
// collect any object's iterable properties.
childEntries.push([key, value]);
} else {
// aggregate any object's non iterable
// properties at data node level.
(dataNode ??= {
id: getId(),
data: { label: {} }
}).data.label[key] = value;
}
return { dataNode, childEntries };
}, { dataNode: null, childEntries: [] });
if (dataNode !== null) {
putNodeData(dataNode);
}
childEntries
.forEach(([key, value]) => {
// every object's iterable property is supposed
// to be created as an own parent entity.
dataNode = {
id: getId(),
data: { label: key },
};
putNodeData(dataNode);
result.push(
...flattenStructureRecursively(value, [], {
getId, parent, edgeItems,
})
);
});
}
if (parent === null) {
// append all additionally collected edge items
// in the end of all the recursion.
result.push(...edgeItems);
}
return result;
}
console.log(
flattenStructureRecursively([{
author: "John Doe",
pseudonym: "J.D.",
books: [{
title: "Book 1",
}, {
title: "Book 2",
chapters: [{
title: "No Way Home",
page: 256,
}],
}],
age: 26,
}])
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
I want to set a boolean value in a nested object structure, based on a list of indexes
The data I want to update looks like this:
let categories = [{
"name": "Cat 1",
"subs": [{
"name": "Sub Cat 1.1",
"subs": [],
"checked": false
},
{
"name": "Sub Cat 1.2",
"subs": [],
"checked": false
}
],
"checked": false
},
{
"name": "Cat 2",
"subs": [],
"checked": false
}
];
The indexes are stored in an array:
let categoryIndexs = [0,1]
So based on categoryIndexs I want to change the checked property of the object with name "Sub Cat 1.2" to true.
It is like transforming the array [0,1] to find categories[0].subs[1] and update its checked property.
So far I managed to get the value and edit it separately, but how to change the categories variable directly?
let categoryIndexs = [0,1]
let tmp_array = categories;
for(var i = 0; i < categoryIndexs.length;i++){
if(i == 0){
tmp_array = tmp_array[categoryIndexs[i]]
} else {
tmp_array = tmp_array.subs[categoryIndexs[i]]
}
}
tmp_array.checked = true
console.log(tmp_array)
The solution needs to be dynamic, so that if "Sub Cat 1.2" would have a nested entry in its own subs array, I can access it using categoryIndexs like [0,1,0]
You can iterate the given indexes in a loop and at the same time go deeper in your data structure, like in this function:
function setChecked(categories, categoryIndexes) {
let obj = null;
for (let index of categoryIndexes) {
obj = categories[index];
categories = obj.subs;
}
obj.checked = true;
}
let categories = [{"name": "Cat 1","subs": [{"name": "Sub Cat 1.1","subs": [],"checked": false},{"name": "Sub Cat 1.2","subs": [],"checked": false}],"checked": false},{"name": "Cat 2","subs": [],"checked": false}];
setChecked(categories, [0,1]);
console.log(categories);
You could use reduce to get the last subs up until the last subs and update the checked property
let categories = [{"name": "Cat 1","subs": [{"name": "Sub Cat 1.1","subs": [],"checked": false},{"name": "Sub Cat 1.2","subs": [],"checked": false}],"checked": false},{"name": "Cat 2","subs": [],"checked": false}];
let categoryIndexs = [0, 1];
function update(categories, indices) {
const last = indices.pop(); // remove the last
const redued = indices.reduce((acc, i) => acc[i].subs, categories);
redued[last].checked = true;
return categories
}
console.log(update(categories, categoryIndexs))
You could reduce the array by looking to the index of the iteration and take either subs or the item.
const
getValue = (subs, indices) => indices.reduce(
({ subs }, i) => subs[i],
{ subs }
);
let categories = [{ name: "Cat 1", subs: [{ name: "Sub Cat 1.1", subs: [], checked: false }, { name: "Sub Cat 1.2", subs: [], checked: false }], checked: false }, { name: "Cat 2", subs: [], checked: false }],
indices = [0, 1];
console.log(getValue(categories, indices, 'subs'));
I am trying to get the key from the value of the Object. I have the following array:
["Test 91", "Test 92", "Demo 1", "Demo 2"]
And I have one object:
{
D123_EMG: {
value: "Test 91",
isArchived: true
}
D21: {
value: "Test 92",
isArchived: false
}
Z6200_EMO: {
value: "Demo 1",
isArchived: true
}
G211_GTH: {
value: "Demo 2",
isArchived: false
}
}
So how can I get key as D123_EMG if the value is Test 91?
I tried this, but not getting proper response
var data = Object.keys(objectData);
var keys = []
for(var i = 0; i < array.length; i++){
for(var j = 0; j < data.length; j++){
if(array[i] === objectData[data[j].value) {
keys.push(objectData[data[j])
}
}
}
Also, can it be optimized since I used two loops or one-liner approach?
You can use filter() in this way:
const values = ["Test 91", "Test 92", "Demo 1", "Demo 2"];
const data = {
D123_EMG: {
value: "Test 91",
isArchived: true
},
D21: {
value: "Test 92",
isArchived: false
},
Z6200_EMO: {
value: "Demo 1",
isArchived: true
},
G211_GTH: {
value: "Demo 2",
isArchived: false
}
}
const keysFound = Object.keys(data).filter(key => values.includes(data[key].value));
console.log(keysFound); // ["D123_EMG", "D21", "Z6200_EMO", "G211_GTH"];
This isn't really related to react. Someone else may have a cleaner solution, but here is one that will work if I understand your question correctly:
let data = {
D123_EMG: {
value: "Test 91",
isArchived: true
},
D21: {
value: "Test 92",
isArchived: false
},
Z6200_EMO: {
value: "Demo 1",
isArchived: true
},
G211_GTH: {
value: "Demo 2",
isArchived: false
}
}
let name = '';
Object.entries(data).forEach((v) => {
// if the object value matches, set the name variable to the key
if (v[1].value == 'Test 91') {
name = v[0];
}
})
console.log(name)
I like to use .reduce() which in this case also works. Read from the MDN documentation:
The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.
And you can combine it with Object.entries() where the documentation states:
The Object.entries() method returns an array of a given object's own enumerable string-keyed property [key, value] pairs, in the same order as that provided by a for...in loop. (The only important difference is that a for...in loop enumerates properties in the prototype chain as well).
See the working solution what I made:
const data = { D123_EMG: { value: "Test 91", isArchived: true }, D21: { value: "Test 92", isArchived: false }, Z6200_EMO: { value: "Demo 1", isArchived: true }, G211_GTH: { value: "Demo 2", isArchived: false } };
const filterValue = 'Test 91';
const entries = Object.entries(data);
const result = entries.reduce((a, c) => c[1].value === filterValue ? c[0] : a, '');
console.log(result);
I hope this helps!
If you're experiencing this problem in a state management store, then this is a sign that the store is not properly designed. Without more information, I can't really recommend an improvement on how to redesign your state.
So, barring a redesign of your state, you may consider creating a map by value like so:
const byValue = Object.keys(data).reduce((accumulator, currentKey) => {
const currentObject = data[currentKey];
currentObject.key = currentKey;
accumulator[currentObject.value] = currentObject;
return accumulator;
}, {});
This produces a map that looks like this:
{
"Test 91": { "value": "Test 91", "isArchived": true, "key": "D123_EMG" },
"Test 92": { "value": "Test 92", "isArchived": false, "key": "D21" },
"Demo 1": { "value": "Demo 1", "isArchived": true, "key": "Z6200_EMO" },
"Demo 2": { "value": "Demo 2", "isArchived": false, "key": "G211_GTH" }
}
With this, you use the value as the lookup key:
const test91 = byValue["Test 91"]
...
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.
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';
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