How do I loop through deeply nested json object - javascript

var City ={
"City1": [{"name": "Place 1"},{"name": "Place 2"},{"name": "Place 3"}],
"City2":[{"name":"My Place 1"},{"name":"My Place 2"},{"name":"My Place 3"}],
"City3":[{"name":"My Place 1"},{"name":"My Place 2"},{"name":"My Place 3"}]
};
I want to use JavaScript to loop to get the first city name, and then loop to get its places, then get the second city and its places and so on.

You could use a for loop to go through all properties of the City object:
for (var key in City) {
if (City.hasOwnProperty(key)) {
var values = City[key];
// "key" will be City1, City2, ...
// "values" will be the corresponding array through which
// you can loop
}
}

function nestedLoop(obj) {
const res = {};
function recurse(obj, current) {
for (const key in obj) {
let value = obj[key];
if(value != undefined) {
if (value && typeof value === 'object') {
recurse(value, key);
} else {
// Do your stuff here to var value
res[key] = value;
}
}
}
}
recurse(obj);
return res;
}

Try utilizing Object.keys() , Array.prototype.forEach()
var City = {
"City1": [{
"name": "Place 1"
}, {
"name": "Place 2"
}, {
"name": "Place 3"
}],
"City2": [{
"name": "My Place 1"
}, {
"name": "My Place 2"
}, {
"name": "My Place 3"
}],
"City3": [{
"name": "My Place 1"
}, {
"name": "My Place 2"
}, {
"name": "My Place 3"
}]
};
Object.keys(City).forEach(function(value, key) {
console.log(value);
City[value].forEach(function(v, k) {
console.log(v.name)
})
});

for (let key in City) {
let value = City[key];
console.log(key);
for (i = 0; i <= value.length; i++) {
console.log(value[i]);
}
}

Related

JS: Check for existence of deep object key and replace value

I am wanting to develop a function to check for the existence of a key within a deep object and then replace the value of that key with a data set from another object.
E.g.
var obj = {
"id": 1,
"component": "Preset 1",
"priority": 1,
"header": {
"modeSet": 2
}
}
const modeSets = [
{
"id": 1
"name": "Mode Set 1"
},
{
"id": 2
"name": "Mode Set 2"
}
]
function obtainModeSets(obj){
//...
}
When the function obtainModeSets runs I'd like to mutate obj so that the value of modeSet within obj equals { "id": 2 "name": "Mode Set 2" }
Does anyone have any suggestions? Thanks
You can use recursion like this
const obj = {
"id": 1,
"component": "Preset 1",
"priority": 1,
"header": {
"modeSet": 2
}
}
const modeSets = [{
"id": 1,
"name": "Mode Set 1"
},
{
"id": 2,
"name": "Mode Set 2"
}
]
function obtainModeSets(obj) {
Object.entries(obj).forEach(([key, value]) => {
if (key === "modeSet") {
obj[key] = modeSets.find(set => set.id === value)
return
}
if (typeof value === "object") {
obtainModeSets(value)
}
})
}
obtainModeSets(obj)
console.log(obj)
I think something like the below code maybe solves your problem. I don't know what you mean exactly. But based on the showing example, I guess you need to replace your key with the id provided in the modeSets.
function obtainModeSets(obj, key, cb){
// loop through all possible keys
Object.keys(obj).forEach(k => {
if (key === k) {
obj[k] = cb(obj[k])
}
// if the value of the key is object, just go inside
if (typeof obj[k] === "object") {
obtainModeSets(obj[k], key, cb)
}
})
}
// you can call your function like this
obtainModeSets(obj, 'modeSet', (val) => {
return modeSets.find(mode => mode.id === val)
})

How to map JSON object's values, with another object's values when that object is having key similar to the JSON object value

I have a JSON object that I want to change values dynamically with the object that comes from front end(ReactJs) which has same value as the key of that object.here is my JSON object.
var jsonObj = {
"fields": {
"field1": {
"key": "value_1"
},"field2": {
"name": "value_3"
},
"field3":{"accountId":"value_9"},
"field4": "value_2",
"field5": "value_4",
"field6": "value_5",
"field7": {
"value": "value_6"
},
"field8": {
"type": "doc",
"version": 1,
"content": [
{
"type": "paragraph",
"content": [
{
"text": "value_7",
"type": "text"
}
]
}
]
}
}
}
This is my other object that comes from React front-end
Obj1 {
value_4: '896',
value_9: '62cbef88ec233f24600c',
value_5: 'Software',
value_7: 'description from front',
value_6: 'test#gmail.com',
value_1: 'TRDSD',
value_2: 'Test sum',
value_8: '',
value_10: '',
value_11: '',
value_12: '',
value_13: '',
value_3: 'TRDSD_incident'
}
What I want is this,
{
"fields": {
"field1": {
"key": "TRDSD"
},"field2": {
"name": "TRDSD_incident"
},
"field3":{"accountId":"62cbef88ec233f24600c"},
"field4": "Test sum",
"field5": "896",
"field6": "Software",
"field7": {
"value": "test#gmail.com"
},
"field8": {
"type": "doc",
"version": 1,
"content": [
{
"type": "paragraph",
"content": [
{
"text": "description from front",
"type": "text"
}
]
}
]
}
}
}
Already I achieve this using this function.here what I do is first I take keys that have no nested objects into an array and check for each field.also I have same function for nested object keys and check for each in the same way( value,key,name,accountId). and finally I get the JSON object as I expected.
function usingTree2(obj2, map) {
function traverse2(children) {
for (let item of children) {
if (item && item.field4) {
item.field4 = map[item.field4];
}
if (item && item.field5) {
item.field5 = map[item.field5];
}
if (item && item.field6) {
item.field6 = map[item.field6];
}
if (item && item.field8.content[0].content[0].text) {
item.field8.content[0].content[0].text =
map[item.field8.content[0].content[0].text];
}
if (item && item.children) {
traverse2(item.children);
}
}
}
traverse2(obj2);
return obj2;
}
function usingTree(obj, map) {
function traverse(children) {
for (let item of children) {
if (item && item.key) {
item.key = map[item.key];
} else if (item && item.value) {
item.value = map[item.value];
} else if (item && item.name) {
item.name = map[item.name];
} else if (item && item.accountId) {
item.accountId = map[item.accountId];
} else if (item && item.id) {
item.id = map[item.id];
}
if (item && item.children) {
traverse(item.children);
}
}
}
traverse(obj);
return obj;
}
const map = obj1
var obj = Object.values(jsonObj.fields);
var obj2 = Object.values(jsonObj);
usingTree(obj, map);
usingTree2(obj2, map);
for (let i = 0; i < fieldCount; i ++) {
usingTree(obj, map);
}
for (let i = 0; i < fieldCount; i ++) {
usingTree2(obj2, map);
}
But the problem is fields in JSON object are changing. So instead of checking for each field how can I achieve this dynamically.Of course without using same function twice for nested objects. I know this is not a best way. I am new to Nodejs, so please help me to do this. Hope my question is clear. Thanks in advance.

Get values for a matching key recursively from object

I have this json object
{
"data": {
"user": {
"user_info": {
"id": "AoGC2HQ9vedHmzcMX"
},
"product": [
{
"node": {
"id": "NzcxNzU2ODU1ODM1",
"feedback": {
"raters": {
"nodes": [
{
"id": "1",
"name": "Dan"
},
{
"id": "2",
"name": "Allen"
},
{
"id": "3",
"name": "Williams"
}
]
},
"commentors": {
"nodes": [
{
"id": "001",
"name": "Kent"
},
{
"id": "002",
"name": "Jay"
}
]
}
}
}
}
]
}
}
}
So how do I make it to get values of id If the parent property matches the desired key name, In this example I want to get all id's from raters.nodes only.
so expected result is
[1,2,3]
I know can do obj.data.user.product[0].node.feedback.raters.nodes and loop through that, but that is not how I want and the object tree occasionally changes.
I have used this recursive function
const recursiveSearch = (obj, searchKey, results = []) => {
const r = results;
Object.keys(obj).forEach(key => {
const value = obj[key];
if(key === searchKey && typeof value !== 'object'){
r.push(value);
}else if(typeof value === 'object'){
recursiveSearch(value, searchKey, r);
}
});
return r;
};
//returns all id's
While it works, it returns all id values, so how do I improve it? If not, how do I make this possible?
I think you want to really do this in 2 steps,..
First make a function to get the root node your looking for, and then you can just use map like normal.
Below is an example.
var data = JSON.parse("{\"data\":{\"user\":{\"user_info\":{\"id\":\"AoGC2HQ9vedHmzcMX\"},\"product\":[{\"node\":{\"id\":\"NzcxNzU2ODU1ODM1\",\"feedback\":{\"raters\":{\"nodes\":[{\"id\":\"1\",\"name\":\"Dan\"},{\"id\":\"2\",\"name\":\"Allen\"},{\"id\":\"3\",\"name\":\"Williams\"}]},\"commentors\":{\"nodes\":[{\"id\":\"001\",\"name\":\"Kent\"},{\"id\":\"002\",\"name\":\"Jay\"}]}}}}]}}}");
function getRoot(data, search) {
function get(path, data) {
for (const [k, v] of Object.entries(data)) {
if (v instanceof Object) {
const pp = `${path}.${k}`;
if (pp.slice(-search.length) === search) {
return v;
}
const r = get(`${path}.${k}`, v);
if (r) return r;
}
}
}
return get('', data);
}
const r = getRoot(data, 'raters.nodes');
console.log(r && r.map(i => i.id));

Compare two arrays and update with the new values by keeping the existing objects using javascript

Below are my two arrays .I want to compare them and the resultant array should contain the updated values.Id's are common..
The arrays spans to n levels ie., there is no fixed levels..
The first array ie., the array before updation..
var parentArray1=[
{
"id": 1,
"name": "test",
"context": [
{
"id": 1.1,
"name": "test 1.1"
}
]
},
{
"id": 2,
"name": "test"
},
{
"id": 3,
"name": "test",
"context": [
{
"id": 3.1,
"name": "test 3.1"
}
]
},
{
"id": 4,
"name": "test"
}
]
The operations that i performed are
1.Adding a new Item
2.Updating an existing item
As a result of these two operations the changed values I will be getting in a different array..
ie.,
var changedArray=
[
{
"id": 1,
"name": "test1",
"context": [
{
"id": 1.1,
"name": "Changed test 1.1"
}
]
},
{
"id": 5,
"name": "test5"
}
]
Now I have written a generic function that loops through the parentArray1 and using the unique propertiesI need to either add a new item,if the item is there in the changedArray or update an existing item at any level
The resultant array should be ..
[
{
"id": 1,
"name": "test",
"context": [
{
"id": 1.1,
"name": "Changed test 1.1"
}
]
},
{
"id": 2,
"name": "test"
},
{
"id": 3,
"name": "test",
"context": [
{
"id": 3.1,
"name": "test 3.1"
}
]
},
{
"id": 4,
"name": "test"
},
{
"id": 5,
"name": "test5"
}
]
Generic function:
compareArray(parentArray1, changedArray, ["id"]);
function compareArray(array1, array2, propertyArray) {
var newItem = new Array();
array2.map(function(a1Item) {
array1.map(function(a2Item) {
/ If array loop again /
if (a2Item.constructor === Array) {
compareArray(a2Item, a1Item)
} else {
/ loop the property name to validate /
propertyArray.map(function(property) {
if (a2Item[property]) {
if (a2Item[property] === a1Item[property]) {
a2Item = a1Item
} else {
var isAvailable = _.find(newItem, function(item) {
return item[property] === a1Item[property]
})
if (!isAvailable) {
newItem.push(a1Item);
}
}
}
})
}
});
});
/ Insert the new item into the source array /
newItem.map(function(item) {
array1.push(item);
});
console.log("After Compare : " + array1);
}
I suggest to use a temporary object for the reference to the id and update if exist or push if not exist.
var parentArray1 = [{ "id": 1, "name": "test", "context": [{ "id": 1.1, "name": "test 1.1" }] }, { "id": 2, "name": "test" }, { "id": 3, "name": "test", "context": [{ "id": 3.1, "name": "test 3.1" }] }, { "id": 4, "name": "test" }],
changedArray = [{ "id": 1, "name": "test1", "context": [{ "id": 1.1, "name": "Changed test 1.1" }] }, { "id": 5, "name": "test5" }];
function insert(array, data) {
function iter(array) {
array.forEach(function (a) {
if (!('id' in a)) {
return;
}
if (o[a.id] !== a) {
o[a.id] = a;
}
Object.keys(a).forEach(function (k) {
Array.isArray(a[k]) && iter(a[k]);
});
});
}
var o = {};
iter(array);
data.forEach(function (a) {
if (o[a.id]) {
Object.keys(a).forEach(function (k) {
o[a.id][k] = a[k];
});
return;
}
array.push(a);
});
}
insert(parentArray1, changedArray);
document.write('<pre>' + JSON.stringify(parentArray1, 0, 4) + '</pre>');
This is what I came up with:
function sameKeys(o1, o2, keys) {
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (!o1.hasOwnProperty(key) || !o2.hasOwnProperty(key))
throw 'compared objects do not have the key ' + key;
if (o1[key] !== o2[key])
return false;
}
return true;
}
function isNothing(o) {
return typeof(o) === 'undefined' || o === null;
}
// this does not work if objects have functions as properties
function clone(o) {
if (isNothing(o))
return o;
return JSON.parse(JSON.stringify(o));
}
function extend(o1, o2, keys) {
if (isNothing(o2))
return;
if (isNothing(o1))
throw ('first parameter cannot be empty');
if (typeof(o1) != 'object' || typeof(o2) != 'object')
throw ('extend only works on objects');
Object.keys(o2).forEach(function (key) {
var newVal = o2[key];
if (o1.hasOwnProperty(key)) {
if (isNothing(newVal)) {
delete o1[key];
} else
if (Array.isArray(newVal)) {
compareArray(o1[key], newVal, keys);
} else {
switch (typeof(newVal)) {
case 'object':
extend(o1[key], newVal, keys);
break;
case 'boolean':
case 'number':
case 'string':
o1[key] = newVal;
break;
default:
throw 'not supported property type: ' + typeof(newVal);
}
}
} else {
o1[key] = clone(newVal);
}
});
}
function removeFromArray(arr, ids, keyArray) {
var indexes = [];
var it1s = arr.forEach(function (it, idx) {
if (sameKeys(ids, it, keyArray)) {
indexes.push(idx);
} else {
Object.keys(it).forEach(function (key) {
var newVal = it[key];
if (Array.isArray(newVal)) {
removeFromArray(it[key], ids, keyArray);
}
});
}
});
if (indexes.length) {
if (indexes.length > 1)
throw 'found multiple possible objects for the same key combination'
arr.splice(indexes[0], 1);
}
}
function compareArray(a1, a2, keyArray) {
a2.forEach(function (it2) {
var it1s = a1.filter(function (it) {
return sameKeys(it2, it, keyArray);
});
var it1;
if (!it1s.length) {
it1 = clone(it2);
a1.push(it1);
} else {
if (it1s.length > 1)
throw 'found multiple possible objects for the same key combination'
it1 = it1s[0];
extend(it1, it2, keyArray);
}
if (it2.removedIds) {
it2.removedIds.forEach(function (ids) {
removeFromArray(a1, ids, keyArray);
});
}
});
}
Use it with compareArray(parentArray1,changedArray,['id']);
Note that it would not work with objects that contain functions. Also, if the arrays would be large, perhaps a better solution is to sort both arrays by key, then always look from the last found object up. That's all I got for now.
Updated it with some concepts from Nina and some clearing of the code.
As I understood it, you only want to add properties. So extend({a: {b: 2}},{a:{c:3}}) will result in {a: {b:2,c:3}}. If this is not what you wanted, let me know.
I also added functionality for removing ids. If any of the objects in the array contains a removedIds array of the form [{id: 4},{id: 5}] then the items with those ids will be removed from the original array.
Slight modification on code, to satisfy your conditions. Try it!
function compareArray(originalArray, destinationArray, propertyArray) {
var newItem = new Array(), processedItem = new Array();
for (var i = 0; i < originalArray.length; i++) {
var sourceElement = originalArray[i];
for (var j = 0; j < destinationArray.length; j++) {
var destinationElement = destinationArray[j];
var isUpdated = false;
if (sourceElement.constructor === Array) {
compareArray(sourceElement, destinationElement, propertyArray);
} else {
/* loop the property name to validate */
propertyArray.map(function(property) {
if (sourceElement[property]) {
if (sourceElement[property] === destinationElement[property]) {
originalArray[i] = _.clone(destinationElement);
isUpdated = true;
return;
} else {
var isAvailable = _.find(newItem, function(item) {
return item[property] === destinationElement[property];
});
if (!isAvailable) {
var isAlreadyProcessed = _.find(processedItem, function(item) {
return item[property] === destinationElement[property];
});
if(!isAlreadyProcessed){
newItem.push(destinationElement);
}
}
}
}
});
}
if (isUpdated === true) {
break;
}
}
processedItem.push(sourceElement);
}
newItem.map(function(item) {
originalArray.push(item);
});
return originalArray;
}

How to display a specifc path from JSON tree depending on the leaves values?

I have a question regarding displaying a path from a tree depending on the value of the leaf, for example I have the following JSON :
{
"children":
[
{
"children":
[
{
"name": "Predict Conversion"
}
],
"name": "Browser ID in {1}"
},
{
"children":
[
{
"name": "Predict Click"
}
],
"name": "Browser ID not in {1}"
}
],
"name": "Device Type ID in {1,3,4}"
}
I want to display only the full path leading to the leaf with value = "Predict Conversion"
You can use recursion to loop over object. Use Array.isArray to test if value is of type Array and typeof(obj)==="object" for object.
Note: typeof(obj) will return object for both Array and for Object
function searchInObj(obj, value, result) {
// check for array and call for every item
if (Array.isArray(obj)) {
// primary flag for array.
var r = false;
obj.forEach(function(item, index) {
// temporary flag for every iteration.
var _r = searchInObj(item, value, result);
if (_r) result.push(index)
// if one of element returned true, array should return true.
r = _r || r;
});
return r;
}
// If Object, loop over properties
else if (typeof(obj) === "object") {
for (var k in obj) {
// If object, check if property is Object/Array and call self.
if (typeof(obj[k]) === "object") {
var r = searchInObj(obj[k], value, result);
if (r) result.push(k);
return r;
}
// If property is not Array/Object, match value
else if (obj[k] === value) {
result.push(k);
return true;
}
// If no match, return false
else {
return false;
}
}
}
}
var data = {
"children": [{
"children": [{
"name": "Predict Conversion"
}],
"name": "Browser ID in {1}"
}, {
"children": [{
"name": "Predict Click"
}],
"name": "Browser ID not in {1}"
}],
"name": "Device Type ID in {1,3,4}"
}
var result = []
searchInObj(data, "Predict Conversion", result);
document.write("<pre>" + JSON.stringify(result.reverse(), 0, 4) + "</pre>");
Note: For small JSON, this will work but if your JSON is very long, this can be very expensive operation.
You can use recursion for it.
Quick example
var tree = {
"children": [{
"children": [{
"name": "Predict Conversion"
}],
"name": "Browser ID in {1}"
}, {
"children": [{
"name": "Predict Click"
}],
"name": "Browser ID not in {1}"
}],
"name": "Device Type ID in {1,3,4}"
};
console.debug(tree);
function getPath(node, value){
if(typeof node.children !== "undefined" && node.children !== null){
for(var index in node.children){
var name = getPath(node.children[index], value);
if(name) {
return node.name+"."+name;
}
}
} else {
if(node.name === value){
return node.name;
}
return false;
}
}
console.log(getPath(tree, "Predict Conversion"))
Working example in this fiddle

Categories

Resources