Iterate through JSON and change arrays to strings - javascript

I have a JSON object that was returned from an XML to js function. This xml converter creates arrays for every entry even when they should be strings. I cannot modify this original function so therefore I would like to take my final json object, iterate through it, detect if a value is an array of length 1 and, if so, change that array to a string.
Original object:
var json = {
user: {
name: ["bob"],
email: ["bob#example.org"]
},
items: [{
name: ["Object 1"]
},{
name: ["Object 2"]
}]
}
Should become:
var json = {
user: {
name: "bob",
email: "bob#example.org"
},
items: [{
name: "Object 1"
},{
name: "Object 2"
}]
}
I have considered the reviver function but a) I would like to avoid going back to a string and b) I am not sure if that would even work as it will probably just feed me each array element individually.

This recursive function seems to work for this problem:
function simplify(obj) {
for (var k in obj) {
if (Object.prototype.toString.call(obj[k]) == '[object Array]' && obj[k].length == 1) {
obj[k] = obj[k][0];
}
else if (typeof obj[k] == 'object') {
obj[k] = simplify(obj[k]);
}
}
return obj;
}
simplify(json);
Demo: http://jsfiddle.net/xkz4W/

Here's a recursive way to do it:
function flattenArrays(data) {
function processItem(item) {
if (Array.isArray(item)) {
if (item.length === 1 && typeof item[0] === "string") {
data[prop] = item[0];
} else {
for (var i = 0; i < item.length; i++) {
processItem(item[i]);
}
}
} else if (typeof item === "object") {
flattenArrays(item);
}
}
for (var prop in data) {
processItem(data[prop]);
}
}
Working demo: http://jsfiddle.net/jfriend00/L5WKs/

Related

Replace nested field values in an object

I need to replace every time value in a nested object with a momentJS element of its value.
const input = {
data: {
sub1: {
time: 1578857603218
}
sub2: {
some: 'thing,
foo: [{
value: 123,
time: 1578857603218
}]
}
}
}
Right now my code looks very ugly, as I'm doing this manually as there are specific fields with an optional time value.
if (data && data.sub2 && data.sub2.foo && data.sub2.foo[0].time) {
data.sub2.foo[0].time = moment(data.sub2.foo[0].time).toDate()
}
To do this in a more dynamic way, I see two options:
Pass something like an array/map with all optional time fields and replace them with a loop
Is there a better way to replace my if conditions to go through all relacing time fields?
Iterate through all keys
But this would not work for nested objects.
for (var prop in obj) {
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
// do stuff
}
}
If you know the key value pair, then what you show as already in use is exactly what you should use. It is O(1), very quick, and essentially a single line.
Making that dynamic will require O(n) where n is the number of key value pairs, and will require several lines of code.
const renderTime = input => {
if (Array.isArray(input)) {
input.forEach(el => renderTime(el));
}
if (typeof input === 'object' && !!input) {
Object.keys(input).forEach(k => {
if (k === 'time') input[k] = 'moment(input[k]).toDate()';
else renderTime(input[k]);
});
}
};
const input = {
data: {
sub1: {
time: 1578857603218
},
sub2: {
some: 'thing',
foo: [{
value: 123,
time: 1578857603218
}]
}
}
};
renderTime(input);
console.log(input);
Whenever you want to deal with nested objects with an undetermined level of depth, think of recursivity
const setTime = (object, time) => {
for (let prop in object) {
if (!Object.prototype.hasOwnProperty.call(object, prop)) {
continue;
}
if (typeof (object[prop]) === 'object') {
setTime(object[prop], time);
}
if (prop === 'time') {
object.time = time;
}
}
return object;
};
const input = {
data: {
sub1: {
time: 1578857603218
},
sub2: {
some: 'thing',
foo: [{
value: 123,
time: 1578857603218
}]
}
}
}
setTime(input, 666);
Try this one:) The secret is recursive loop
const input = {
data: {
sub1: {
time: 1578857603218
},
sub2: {
some: 'thing',
foo: [{
value: 123,
time: 1578857603218
}]
}
}
};
function changeTime(obj) {
obj && Object.keys(obj).forEach(key => {
if(key === 'time') {
obj.time = moment(obj.time); // you have to use moment
}
if(typeof obj[key] === 'object'){
changeTime(obj[key]);
}
});
}
changeTime(input);
console.log(input);
This won't handle the case where the nested fields are in an array, but it should work for nested objects
function replaceNestedValues(obj, targetVal, nextVal) {
return Object.keys(obj).reduce((acc, key) => {
const value = obj[key];
if (typeof value === 'object' && value !== null) {
acc[key] = replaceNestedValues(value, targetVal, nextVal);
} else if (value === targetVal) {
acc[key] = nextVal;
} else {
acc[key] = value;
}
return acc;
}, {});
}
Example
const data = { a: { b: 10, c: null }, d: null };
replaceNestedValues(data, null, '');
// => { a: { b: 10, c: '' }, d: '' }
You can use this code to modify your property. The property can be present in deeply nested object or within array of objects.
foo(entry: any | any[]) {
if (Array.isArray(entry)) {
entry.forEach(ent => this.foo(ent));
} else if (typeof entry === 'object' && entry !== null) {
for (let [key, value] of Object.entries(entry)) {
if (typeof value === 'object' && value !== null) {
this.foo(value);
} else if (Array.isArray(value)) {
this.foo(value);
}
if (key === "time") {
entry[key] = "changed"; // modified value
}
}
return entry;
}
}
this.foo(data); // your object

How to check if array has nested property with defined value

I have an array of complicated objects and arrays in javascript such as:
var array = [
{ "simpleProp": "some value" },
{ "booleanProp": false },
{
"arrayProp": [
{ "prop1": "value1" },
{
"prop2": {
"prop22": "value22",
"prop23": "value23"
}
},
{ "prop3": "value3" },
{ "booleanProp": true }
]
}
];
I have to know if there is a property with defined value in my array, such as:
function some(array, property, value) {
//some logic here
// return boolean
};
That is, for my source array the result of this:
var result = some(array, "booleanProp", true)
- must be TRUE.
I tried to use lodash function _.some(), but it returns false for my array, it appears _.some() can't find deeply nested properties.
It would be very cool if the function may support complicated object as source, not only array.
I'd appreciate any help, thanks.
You could use an iterative and recursive approach by checking the actual object and if the value is an object iterate the object's keys.
function some(object, property, value) {
return object[property] === value || Object.keys(object).some(function (k) {
return object[k] && typeof object[k] === 'object' && some(object[k], property, value);
});
}
var data = [{ simpleProp: "some value" }, { booleanProp: false }, { arrayProp: [{ prop1: "value1" }, { prop2: { prop22: "value22", prop23: "value23" } }, { prop3: "value3" }, { booleanProp: true }] }];
console.log(some(data, 'booleanProp', true)); // true
console.log(some(data, 'foo', 42)); // false
Above Solution is great but it is not working for Array.
So I've Modified it little bit and now it is working for both Arrays & normal properties. Even In Arrays element's placement can be anything.
const data = {
"names": [
{
"name": {
'homename': 'Raju',
'academisName': 'Rajpal',
'callingName': ['Raj', 'Rajpal', 'Raju']
},
"defaultName": "Raj"
}]
}
Code for Array:
const some = (object, property, value) => {
return _.isArray(value) && _.isEqual(_.sortBy(object[property]), _.sortBy(value)) || object[property] === value || Object.keys(object).some(function (k) {
return object[k] && typeof object[k] === 'object' && some(object[k], property, value);
});
}
const data = {
"names": [{
"name": {
'homename': 'Raju',
'academisName': 'Rajpal',
'callingName': ['Raj', 'Rajpal', 'Raju']
},
"defaultName": "Raj"
}]
}
const some = (object, property, value) => {
return _.isArray(value) && _.isEqual(_.sortBy(object[property]), _.sortBy(value)) || object[property] === value || Object.keys(object).some(function(k) {
return object[k] && typeof object[k] === 'object' && some(object[k], property, value);
});
}
console.log('Result 1', some(data, 'callingName', ["Raj", "Rajpal", "Raju"]));
console.log('Result 2', some(data, 'callingName', ["Rajpal", "Raj", "Raju"]));
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.js"></script>
Note: that value.sort() will mutate the arrays so I've used _.sortBy(value), same for object[property]
console.log(some(data, 'callingName',  ["Raj", "Rajpal", "Raju"]));
console.log(some(data, 'callingName', ["Rajpal", "Raj", "Raju"]));

Javascript reflection: Get nested objects path

In this stackoverflow thread, i learnt you can get a object path via a simple string.
Accessing nested JavaScript objects with string key
consider the following:
var person = { name: "somename", personal: { weight: "150", color: "dark" }};
var personWeight = deep_value(person,"personal.weight");
I an trying to construct an array of the object values who are not of type 'object' from my 'person' object.
Hence the array would look like:
[['name', []],['personal.weight', []],['personal.color', []]];
I want them to look in that form because i have further use for it down the road.
That's what I've tried:
var toIterate = { name: "somename", personal: { age: "19", color: "dark" } }
var myArray = [];
$.each(toIterate, recursive);
function recursive(key, value) {
if (key !== null) {
myArray.push([key, []]);
}
else {
$.each(value, recursive);
}
}
console.log(myArray);
Just use recursion to walk the object.
var person = {
name: "somename",
personal: {
weight: "150",
color: "dark",
foo: {
bar: 'bar',
baz: 'baz'
},
empty: {
}
}
};
// however you want to do this
var isobject = function(x){
return Object.prototype.toString.call(x) === '[object Object]';
};
var getkeys = function(obj, prefix){
var keys = Object.keys(obj);
prefix = prefix ? prefix + '.' : '';
return keys.reduce(function(result, key){
if(isobject(obj[key])){
result = result.concat(getkeys(obj[key], prefix + key));
}else{
result.push(prefix + key);
}
return result;
}, []);
};
var keys = getkeys(person);
document.body.innerHTML = '<pre>' + JSON.stringify(keys) + '</pre>';
Then use Array.prototype.map to massage the array of keys into your preferred format.
Note the behaviour with person.personal.empty.
This does seem like a strange way to store an object's keys. I wonder what your 'further use for it down the road' is.
This is what worked for me. Note that, a raw map is created first and then mapped to an join the items in the Array with ..
var toIterate = {
name: "somename",
personal: {
age: "19",
color: "dark"
}
};
console.log(getObjPath(toIterate).map(item => item.join('.')));
function isObject(x) {
return Object.prototype.toString.call(x) === '[object Object]';
};
function getObjPath(obj, pathArray, busArray) {
pathArray = pathArray ? pathArray : [];
if (isObject(obj)) {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (isObject(obj[key])) {
busArray = busArray ? bussArray : [];
busArray.push(key);
getObjPath(obj[key], pathArray, busArray);
} else {
if (busArray) {
pathArray.push(busArray.concat([key]));
} else {
pathArray.push([key]);
}
}
}
}
}
return pathArray;
}
Good Luck...
I found the following solution on github.
https://github.com/mariocasciaro/object-path

How to find a node in a tree with JavaScript

I have and object literal that is essentially a tree that does not have a fixed number of levels. How can I go about searching the tree for a particualy node and then return that node when found in an effcient manner in javascript?
Essentially I have a tree like this and would like to find the node with the title 'randomNode_1'
var data = [
{
title: 'topNode',
children: [
{
title: 'node1',
children: [
{
title: 'randomNode_1'
},
{
title: 'node2',
children: [
{
title: 'randomNode_2',
children:[
{
title: 'node2',
children: [
{
title: 'randomNode_3',
}]
}
]
}]
}]
}
]
}];
Basing this answer off of #Ravindra's answer, but with true recursion.
function searchTree(element, matchingTitle){
if(element.title == matchingTitle){
return element;
}else if (element.children != null){
var i;
var result = null;
for(i=0; result == null && i < element.children.length; i++){
result = searchTree(element.children[i], matchingTitle);
}
return result;
}
return null;
}
Then you could call it:
var element = data[0];
var result = searchTree(element, 'randomNode_1');
Here's an iterative solution:
var stack = [], node, ii;
stack.push(root);
while (stack.length > 0) {
node = stack.pop();
if (node.title == 'randomNode_1') {
// Found it!
return node;
} else if (node.children && node.children.length) {
for (ii = 0; ii < node.children.length; ii += 1) {
stack.push(node.children[ii]);
}
}
}
// Didn't find it. Return null.
return null;
Here's an iterative function using the Stack approach, inspired by FishBasketGordo's answer but taking advantage of some ES2015 syntax to shorten things.
Since this question has already been viewed a lot of times, I've decided to update my answer to also provide a function with arguments that makes it more flexible:
function search (tree, value, key = 'id', reverse = false) {
const stack = [ tree[0] ]
while (stack.length) {
const node = stack[reverse ? 'pop' : 'shift']()
if (node[key] === value) return node
node.children && stack.push(...node.children)
}
return null
}
This way, it's now possible to pass the data tree itself, the desired value to search and also the property key which can have the desired value:
search(data, 'randomNode_2', 'title')
Finally, my original answer used Array.pop which lead to matching the last item in case of multiple matches. In fact, something that could be really confusing. Inspired by Superole comment, I've made it use Array.shift now, so the first in first out behavior is the default.
If you really want the old last in first out behavior, I've provided an additional arg reverse:
search(data, 'randomNode_2', 'title', true)
My answer is inspired from FishBasketGordo's iterativ answer. It's a little bit more complex but also much more flexible and you can have more than just one root node.
/**searchs through all arrays of the tree if the for a value from a property
* #param aTree : the tree array
* #param fCompair : This function will receive each node. It's upon you to define which
condition is necessary for the match. It must return true if the condition is matched. Example:
function(oNode){ if(oNode["Name"] === "AA") return true; }
* #param bGreedy? : us true to do not stop after the first match, default is false
* #return an array with references to the nodes for which fCompair was true; In case no node was found an empty array
* will be returned
*/
var _searchTree = function(aTree, fCompair, bGreedy){
var aInnerTree = []; // will contain the inner children
var oNode; // always the current node
var aReturnNodes = []; // the nodes array which will returned
// 1. loop through all root nodes so we don't touch the tree structure
for(keysTree in aTree) {
aInnerTree.push(aTree[keysTree]);
}
while(aInnerTree.length > 0) {
oNode = aInnerTree.pop();
// check current node
if( fCompair(oNode) ){
aReturnNodes.push(oNode);
if(!bGreedy){
return aReturnNodes;
}
} else { // if (node.children && node.children.length) {
// find other objects, 1. check all properties of the node if they are arrays
for(keysNode in oNode){
// true if the property is an array
if(oNode[keysNode] instanceof Array){
// 2. push all array object to aInnerTree to search in those later
for (var i = 0; i < oNode[keysNode].length; i++) {
aInnerTree.push(oNode[keysNode][i]);
}
}
}
}
}
return aReturnNodes; // someone was greedy
}
Finally you can use the function like this:
var foundNodes = _searchTree(data, function(oNode){ if(oNode["title"] === "randomNode_3") return true; }, false);
console.log("Node with title found: ");
console.log(foundNodes[0]);
And if you want to find all nodes with this title you can simply switch the bGreedy parameter:
var foundNodes = _searchTree(data, function(oNode){ if(oNode["title"] === "randomNode_3") return true; }, true);
console.log("NodeS with title found: ");
console.log(foundNodes);
FIND A NODE IN A TREE :
let say we have a tree like
let tree = [{
id: 1,
name: 'parent',
children: [
{
id: 2,
name: 'child_1'
},
{
id: 3,
name: 'child_2',
children: [
{
id: '4',
name: 'child_2_1',
children: []
},
{
id: '5',
name: 'child_2_2',
children: []
}
]
}
]
}];
function findNodeById(tree, id) {
let result = null
if (tree.id === id) {
return tree;
}
if (Array.isArray(tree.children) && tree.children.length > 0) {
tree.children.some((node) => {
result = findNodeById(node, id);
return result;
});
}
return result;}
You have to use recursion.
var currChild = data[0];
function searchTree(currChild, searchString){
if(currChild.title == searchString){
return currChild;
}else if (currChild.children != null){
for(i=0; i < currChild.children.length; i ++){
if (currChild.children[i].title ==searchString){
return currChild.children[i];
}else{
searchTree(currChild.children[i], searchString);
}
}
return null;
}
return null;
}
ES6+:
const deepSearch = (data, value, key = 'title', sub = 'children', tempObj = {}) => {
if (value && data) {
data.find((node) => {
if (node[key] == value) {
tempObj.found = node;
return node;
}
return deepSearch(node[sub], value, key, sub, tempObj);
});
if (tempObj.found) {
return tempObj.found;
}
}
return false;
};
const result = deepSearch(data, 'randomNode_1', 'title', 'children');
This function is universal and does search recursively.
It does not matter, if input tree is object(single root), or array of objects (many root objects). You can configure prop name that holds children array in tree objects.
// Searches items tree for object with specified prop with value
//
// #param {object} tree nodes tree with children items in nodesProp[] table, with one (object) or many (array of objects) roots
// #param {string} propNodes name of prop that holds child nodes array
// #param {string} prop name of searched node's prop
// #param {mixed} value value of searched node's prop
// #returns {object/null} returns first object that match supplied arguments (prop: value) or null if no matching object was found
function searchTree(tree, nodesProp, prop, value) {
var i, f = null; // iterator, found node
if (Array.isArray(tree)) { // if entry object is array objects, check each object
for (i = 0; i < tree.length; i++) {
f = searchTree(tree[i], nodesProp, prop, value);
if (f) { // if found matching object, return it.
return f;
}
}
} else if (typeof tree === 'object') { // standard tree node (one root)
if (tree[prop] !== undefined && tree[prop] === value) {
return tree; // found matching node
}
}
if (tree[nodesProp] !== undefined && tree[nodesProp].length > 0) { // if this is not maching node, search nodes, children (if prop exist and it is not empty)
return searchTree(tree[nodesProp], nodesProp, prop, value);
} else {
return null; // node does not match and it neither have children
}
}
I tested it localy and it works ok, but it somehow won't run on jsfiddle or jsbin...(recurency issues on those sites ??)
run code :
var data = [{
title: 'topNode',
children: [{
title: 'node1',
children: [{
title: 'randomNode_1'
}, {
title: 'node2',
children: [{
title: 'randomNode_2',
children: [{
title: 'node2',
children: [{
title: 'randomNode_3',
}]
}]
}]
}]
}]
}];
var r = searchTree(data, 'children', 'title', 'randomNode_1');
//var r = searchTree(data, 'children', 'title', 'node2'); // check it too
console.log(r);
It works in http://www.pythontutor.com/live.html#mode=edit (paste the code)
no BS version:
const find = (root, title) =>
root.title === title ?
root :
root.children?.reduce((result, n) => result || find(n, title), undefined)
This is basic recursion problem.
window.parser = function(searchParam, data) {
if(data.title != searchParam) {
returnData = window.parser(searchParam, children)
} else {
returnData = data;
}
return returnData;
}
here is a more complex option - it finds the first item in a tree-like node with providing (node, nodeChildrenKey, key/value pairs & optional additional key/value pairs)
const findInTree = (node, childrenKey, key, value, additionalKey?, additionalValue?) => {
let found = null;
if (additionalKey && additionalValue) {
found = node[childrenKey].find(x => x[key] === value && x[additionalKey] === additionalValue);
} else {
found = node[childrenKey].find(x => x[key] === value);
}
if (typeof(found) === 'undefined') {
for (const item of node[childrenKey]) {
if (typeof(found) === 'undefined' && item[childrenKey] && item[childrenKey].length > 0) {
found = findInTree(item, childrenKey, key, value, additionalKey, additionalValue);
}
}
}
return found;
};
export { findInTree };
Hope it helps someone.
A flexible recursive solution that will work for any tree
// predicate: (item) => boolean
// getChildren: (item) => treeNode[]
searchTree(predicate, getChildren, treeNode) {
function search(treeNode) {
if (!treeNode) {
return undefined;
}
for (let treeItem of treeNode) {
if (predicate(treeItem)) {
return treeItem;
}
const foundItem = search(getChildren(treeItem));
if (foundItem) {
return foundItem;
}
}
}
return search(treeNode);
}
find all parents of the element in the tree
let objects = [{
id: 'A',
name: 'ObjA',
children: [
{
id: 'A1',
name: 'ObjA1'
},
{
id: 'A2',
name: 'objA2',
children: [
{
id: 'A2-1',
name: 'objA2-1'
},
{
id: 'A2-2',
name: 'objA2-2'
}
]
}
]
},
{
id: 'B',
name: 'ObjB',
children: [
{
id: 'B1',
name: 'ObjB1'
}
]
}
];
let docs = [
{
object: {
id: 'A',
name: 'docA'
},
typedoc: {
id: 'TD1',
name: 'Typde Doc1'
}
},
{
object: {
id: 'A',
name: 'docA'
},
typedoc: {
id: 'TD2',
name: 'Typde Doc2'
}
},
{
object: {
id: 'A1',
name: 'docA1'
},
typedoc: {
id: 'TDx1',
name: 'Typde Doc x1'
}
},
{
object: {
id: 'A1',
name: 'docA1'
},
typedoc: {
id: 'TDx2',
name: 'Typde Doc x1'
}
},
{
object: {
id: 'A2',
name: 'docA2'
},
typedoc: {
id: 'TDx2',
name: 'Type de Doc x2'
}
},
{
object: {
id: 'A2-1',
name: 'docA2-1'
},
typedoc: {
id: 'TDx2-1',
name: 'Type de Docx2-1'
},
},
{
object: {
id: 'A2-2',
name: 'docA2-2'
},
typedoc: {
id: 'TDx2-2',
name: 'Type de Docx2-2'
},
},
{
object: {
id: 'B',
name: 'docB'
},
typedoc: {
id: 'TD1',
name: 'Typde Doc1'
}
},
{
object: {
id: 'B1',
name: 'docB1'
},
typedoc: {
id: 'TDx1',
name: 'Typde Doc x1'
}
}
];
function buildAllParents(doc, objects) {
for (let o = 0; o < objects.length; o++) {
let allParents = [];
let getAllParents = (o, eleFinded) => {
if (o.id === doc.object.id) {
doc.allParents = allParents;
eleFinded = true;
return { doc, eleFinded };
}
if (o.children) {
allParents.push(o.id);
for (let c = 0; c < o.children.length; c++) {
let { eleFinded, doc } = getAllParents(o.children[c], eleFinded);
if (eleFinded) {
return { eleFinded, doc };
} else {
continue;
}
}
}
return { eleFinded };
};
if (objects[o].id === doc.object.id) {
doc.allParents = [objects[o].id];
return doc;
} else if (objects[o].children) {
allParents.push(objects[o].id);
for (let c = 0; c < objects[o].children.length; c++) {
let eleFinded = null;`enter code here`
let res = getAllParents(objects[o].children[c], eleFinded);
if (res.eleFinded) {
return res.doc;
} else {
continue;
}
}
}
}
}
docs = docs.map(d => buildAllParents(d, objects`enter code here`))
This is an iterative breadth first search. It returns the first node that contains a child of a given name (nodeName) and a given value (nodeValue).
getParentNode(nodeName, nodeValue, rootNode) {
const queue= [ rootNode ]
while (queue.length) {
const node = queue.shift()
if (node[nodeName] === nodeValue) {
return node
} else if (node instanceof Object) {
const children = Object.values(node)
if (children.length) {
queue.push(...children)
}
}
}
return null
}
It would be used like this to solve the original question:
getParentNode('title', 'randomNode_1', data[0])
Enhancement of the code based on "Erick Petrucelli"
Remove the 'reverse' option
Add multi-root support
Add an option to control the visibility of 'children'
Typescript ready
Unit test ready
function searchTree(
tree: Record<string, any>[],
value: unknown,
key = 'value',
withChildren = false,
) {
let result = null;
if (!Array.isArray(tree)) return result;
for (let index = 0; index < tree.length; index += 1) {
const stack = [tree[index]];
while (stack.length) {
const node = stack.shift()!;
if (node[key] === value) {
result = node;
break;
}
if (node.children) {
stack.push(...node.children);
}
}
if (result) break;
}
if (withChildren !== true) {
delete result?.children;
}
return result;
}
And the tests can be found at: https://gist.github.com/aspirantzhang/a369aba7f84f26d57818ddef7d108682
Wrote another one based on my needs
condition is injected.
path of found branch is available
current path could be used in condition statement
could be used to map the tree items to another object
// if predicate returns true, the search is stopped
function traverse2(tree, predicate, path = "") {
if (predicate(tree, path)) return true;
for (const branch of tree.children ?? [])
if (traverse(branch, predicate, `${path ? path + "/" : ""}${branch.name}`))
return true;
}
example
let tree = {
name: "schools",
children: [
{
name: "farzanegan",
children: [
{
name: "classes",
children: [
{ name: "level1", children: [{ name: "A" }, { name: "B" }] },
{ name: "level2", children: [{ name: "C" }, { name: "D" }] },
],
},
],
},
{ name: "dastgheib", children: [{ name: "E" }, { name: "F" }] },
],
};
traverse(tree, (branch, path) => {
console.log("searching ", path);
if (branch.name === "C") {
console.log("found ", branch);
return true;
}
});
output
searching
searching farzanegan
searching farzanegan/classes
searching farzanegan/classes/level1
searching farzanegan/classes/level1/A
searching farzanegan/classes/level1/B
searching farzanegan/classes/level2
searching farzanegan/classes/level2/C
found { name: 'C' }
In 2022 use TypeScript and ES5
Just use basic recreation and built-in array method to loop over the array. Don't use Array.find() because this it will return the wrong node. Use Array.some() instead which allow you to break the loop.
interface iTree {
id: string;
children?: iTree[];
}
function findTreeNode(tree: iTree, id: string) {
let result: iTree | null = null;
if (tree.id === id) {
result = tree;
} else if (tree.children) {
tree.children.some((node) => {
result = findTreeNode(node, id);
return result; // break loop
});
}
return result;
}
const flattenTree = (data: any) => {
return _.reduce(
data,
(acc: any, item: any) => {
acc.push(item);
if (item.children) {
acc = acc.concat(flattenTree(item.children));
delete item.children;
}
return acc;
},
[]
);
};
An Approach to convert the nested tree into an object with depth 0.
We can convert the object in an object like this and can perform search more easily.
The following is working at my end:
function searchTree(data, value) {
if(data.title == value) {
return data;
}
if(data.children && data.children.length > 0) {
for(var i=0; i < data.children.length; i++) {
var node = traverseChildren(data.children[i], value);
if(node != null) {
return node;
}
}
}
return null;
}

find and change properties with the value undefined

I have a nested javascript object called data. Is there any way to iterate through it and find all properties that have the value undefined and set it to a string, "undefined" insted. JSON doesnt validate if the property value is undefined in IE7. Thanks
{
"Target": "System",
"Systemoperation": "Buy-In",
"Systemorigin": "ABB",
"Subscriptionmode": "Maintain & Evolve",
"Evolveto": "800xA",
"Quoteselection": "Full Quote",
"Commitmenttimeframe": "3 years",
"codevalues": {
"Target": 100,
"Systemoperation": 110,
"Systemorigin": false,
"Subscriptionmode": 4,
"Evolveto": undefined,
"Quoteselection": "fullquote",
"Commitmenttimeframe": 3
},
"Route": [
"System",
"Buy-In",
"ABB",
"Maintain & Evolve",
"800xA",
"Full Quote",
"3 years"
],
"currentsystem": ""
}
Here is evolteto in codevalues undefined. Which does not validate (try http://jsonlint.com/).
If this is your data structure (nested objs):
data = {
a: undefined,
b:{
c: '1',
d: undefined
}
}
Then, this works for you
for (var items in data) {
if (data[items] === undefined) {
data[items] = 'undefined';
continue;
}
for (var i in data[items]) {
if (data[items][i] === undefined) {
data[items][i] = 'undefined';
}
}
}
A very crude search but works,
for(var prop in arr)
{
if(typeof arr[prop] === 'object')
{
for(var subprop in arr[prop])
{
if(typeof arr[prop][subprop] === 'undefined')
arr[prop][subprop] = "undefined";
}
}
}
http://jsfiddle.net/h27dp/1/ - JSON validator also validates the result obtained here as a "Good JSON"
function disp(j) {
for(var key in j) {
if(j.hasOwnProperty(key)) {
if(typeof j[key] === "object") {
disp(j[key]);
}
else {
if ( j[key] === undefined) {
j[key] = "undefined";
} // if undefined key
} // else
} // if hasOwnProperty
} // for
} // function
And then call it with
disp[data];

Categories

Resources