Javascript: Print all paths in a JSON tree including arrays - javascript

Example JSON:
{
name: "A",
childNodes: [{
name: "B",
childProps: [{
prop_list: [{
name: "BS1"
}, {
name: "BS2"
}]
}],
childNodes: [{
name: "C",
childProps: [{
prop_list: [{
name: "CS1"
}, {
name: "CS2"
}]
}],
}]
}],
childProps: [{
prop_list: [{
name: "AS1"
}, {
name: "AS2"
}]
}]
}
I want to print output in the following manner.
A, B, BS1
A, B, BS2
A, B, C, CS1
A, B, C, CS2
A, AS1
A, AS2
Basically, I want to print the path to each leaf node. (And I need to get the name property value for each object)

To solve your problem you must loop over each item in your object and use a recursive function to append nested names to your path string.
Here is an example to get you started
var obj = {
name: "A",
childNodes: [{
name: "B",
childProps: [{
prop_list: [{
name: "BS1"
}, {
name: "BS2"
}]
}],
childNodes: [{
name: "C",
childProps: [{
prop_list: [{
name: "CS1"
}, {
name: "CS2"
}]
}],
}]
}],
childProps: [{
prop_list: [{
name: "AS1"
}, {
name: "AS2"
}]
}]
};
(function findName(o, path = '') {
const isLastLevel = !o.childProps && !o.childNodes;
path += isLastLevel ? o.name : o.name + ', ';
if (o.childProps) {
o.childProps.forEach(props => {
props.prop_list.forEach(prop => {
findName(prop, path);
});
});
}
if (o.childNodes) {
o.childNodes.forEach(node => {
findName(node, path);
});
}
if (isLastLevel) {
console.log(path);
}
})(obj);

Perhaps you could take a more generalized approach to implementing this using recusion, so that your traversal works for any format of input data:
function printPaths(node, path) {
const nodeName = node.name ? (path ? ',' : '') + node.name : ''
const nodePath = (path ? path : '') + nodeName
const nodeChildren = Object.values(node).filter(Array.isArray).flat()
return nodeChildren.length > 0 ? nodeChildren
.map(nodeChild => printPaths(nodeChild, nodePath)).flat() : nodePath
}
This solution is decoupled from your specific format of input data, meaning it will examine the current node object and traverse any array values found, eventually returning all discovered the paths:
var input = {
name: "A",
childNodes: [{
name: "B",
childProps: [{
prop_list: [{
name: "BS1"
}, {
name: "BS2"
}]
}],
childNodes: [{
name: "C",
childProps: [{
prop_list: [{
name: "CS1"
}, {
name: "CS2"
}]
}],
}]
}],
childProps: [{
prop_list: [{
name: "AS1"
}, {
name: "AS2"
}]
}]
}
function printPaths(node, path) {
const nodeName = node.name ? (path ? ',' : '') + node.name : ''
const nodePath = (path ? path : '') + nodeName
const nodeChildren = Object.values(node).filter(Array.isArray).flat()
return nodeChildren.length > 0 ? nodeChildren
.map(nodeChild => printPaths(nodeChild, nodePath)).flat() : nodePath
}
console.log( printPaths(input) )

Related

Return last value with recursion - Javascript

Hi all I have following data:
const section = {
fileds: [
{ id: "some Id-1", type: "user-1" },
{
child: [
{ id: "some Id-2", type: "user-2" },
{ fileds: [{ id: "kxf5", status: "pending" }] },
{ fileds: [{ id: "ed5t", status: "done" }] }
]
},
{
child: [
{ id: "some Id-3", type: "teacher" },
{ fileds: [{ id: "ccfr", status: null }] },
{ fileds: [{ id: "kdpt8", status: "inProgress" }] }
]
}
]
};
and following code:
const getLastIds = (arr) =>
arr.flatMap((obj) => {
const arrayArrs = Object.values(obj).filter((v) => Array.isArray(v));
const arrayVals = Object.entries(obj)
.filter(([k, v]) => typeof v === "string" && k === "id")
.map(([k, v]) => v);
return [...arrayVals, ...arrayArrs.flatMap((arr) => getLastIds(arr))];
});
console.log(getLastIds(section.fileds));
// output is (7) ["some Id-1", "some Id-2", "kxf5", "ed5t", "some Id-3", "ccfr", "kdpt8"]
My code doing following, it printing in new array all ids.
It's working but I don't need all ids.
I need to return only last id in array and I should use recursion.
The output should be
(4) [" "kxf5", "ed5t", "ccfr", "kdpt8"]
P.S. here is my code in codesandbox
Is there a way to solve this problem with recursion? Please help to fix this.
You can do it with reduce.
function getLastIds (value) {
return value.reduce((prev, cur) => {
if (cur.id) {
return [ ...prev, cur.id ];
} else {
let key = ('child' in cur) ? 'child' : 'fileds';
return [ ...prev, ...getLastIds (cur[key]) ]
}
}, []);
}
You could check if a certain key exists and take this property for mapping id if status exists.
const
getValues = data => {
const array = Object.values(data).find(Array.isArray);
return array
? array.flatMap(getValues)
: 'status' in data ? data.id : [];
},
section = { fileds: [{ id: "some Id-1", type: "user-1" }, { child: [{ id: "some Id-2", type: "user-2" }, { fileds: [{ id: "kxf5", status: "pending" }] }, { fileds: [{ id: "ed5t", status: "done" }] }] }, { child: [{ id: "some Id-3", type: "teacher" }, { fileds: [{ id: "ccfr", status: null }] }, { fileds: [{ id: "kdpt8", status: "inProgress" }] }] }] },
result = getValues(section);
console.log(result);

Javascript : Merge objects in array with the same key not working

I am trying to get the same group names to merge together. I have looked around to get an idea of how to do this so I have done some research and I wrote the below code.
This is my implementation that isn't working:
const input = [
{ people: [{ names: "John" }, { names: "Sam" }], group: "one", },
{ people: [{ names: "George" }], group: "one", },
{ people: [{ names: "Bella" }], group: "two",},
];
var output = [];
input.forEach(function(item) {
var existing = output.filter(function(v, i) {
return v.group == item.group;
});
if (existing.length) {
var existingIndex = output.indexOf(existing[0]);
output[existingIndex].people = output[existingIndex].people.concat(item.people);
}
});
console.log(output)
Desired Output:
const output = [
{
people: [{ names: "John" }, { names: "Sam" }, { names: "George" }],
group: "one",
},
{
people: [{ names: "Bella" }],
group: "two",
},
];
Using Array.reduce, you can group by the current array using group key.
And from that grouped object, you can get the result you want.
const input = [
{
people: [{ names: "John" }, { names: "Sam" }],
group: "one",
},
{
people: [{ names: "George" }],
group: "one",
},
{
people: [{ names: "Bella" }],
group: "two",
},
];
const groupByKey = input.reduce((acc, cur) => {
acc[cur.group] ? acc[cur.group].people.push(...cur.people) : acc[cur.group] = cur;
return acc;
}, {});
const output = Object.values(groupByKey);
console.log(output);

Get all parent in a nested object using recursion

I have the following object
const object = {
id: "1",
name: "a",
children: [
{
id: "2",
name: "b",
children: [
{
id: "3",
name: "c"
}
]
},
{
id: "4",
name: "d"
}
]
};
I need a function that accept the object and the id of the last child and return the path, for example, the following call: getPath(object, '3'); should return [{id: 1}, {id: 2}, {id: 3}].
I created the function but I can access only to the first parent.
function getPath(model, id, parent) {
if (model == null) {
return;
}
if (model.id === id) {
console.log(model.id, parent.id)
}
if (model.children) {
model.children.forEach(child => getPath(child, id, model));
}
}
PS: The object has an unknown depth.
You could use a short circuit for iterating the children and hand over the path from the function with the target object.
function getPath(model, id) {
var path,
item = { id: model.id };
if (!model || typeof model !== 'object') return;
if (model.id === id) return [item];
(model.children || []).some(child => path = getPath(child, id));
return path && [item, ...path];
}
const object = { id: "1", name: "a", children: [{ id: "2", name: "b", children: [{ id: "3", name: "c" }] }, { id: "4", name: "d" }] };
console.log(getPath(object, '42')); // undefined
console.log(getPath(object, '3')); // [{ id: 1 }, { id: 2 }, { id: 3 }]
.as-console-wrapper { max-height: 100% !important; top: 0; }
This is pretty close. Consider passing the entire path array in your recursive function. The following is a slightly modified version of what you have that accomplishes this.
function getPath(model, id, path) {
if (!path) {
path = [];
}
if (model == null) {
return;
}
if (model.id === id) {
console.log(model.id, path)
}
if (model.children) {
model.children.forEach(child => getPath(child, id, [...path, model.id]));
}
}
const object = {
id: "1",
name: "a",
children: [
{
id: "2",
name: "b",
children: [
{
id: "3",
name: "c"
}
]
},
{
id: "4",
name: "d"
}
]
};
getPath(object, "3");
const object = {
id: "1",
name: "a",
children: [
{
id: "2",
name: "b",
children: [
{
id: "3",
name: "c"
},
{
id: "5",
name: "c"
}
]
},
{
id: "4",
name: "d"
}
]
};
const getPath = (obj, id, paths = []) => {
if (obj.id == id) return [{ id: obj.id }];
if (obj.children && obj.children.length) {
paths.push({ id: obj.id });
let found = false;
obj.children.forEach(child => {
const temPaths = getPath(child, id);
if (temPaths) {
paths = paths.concat(temPaths);
found = true;
}
});
!found && paths.pop();
return paths;
}
return null;
};
console.log(getPath(object, "5"));
console.log(getPath(object, "2"));
console.log(getPath(object, "3"));
console.log(getPath(object, "4"));
.as-console-row {color: blue!important}

How to get nested parent path from a JSON tree in JavaScript?

I have a JSON tree structure like this.
[
{
"title":"News",
"id":"news"
},
{
"title":"Links",
"id":"links",
"children":[
{
"title":"World",
"id":"world",
"children":[
{
"title":"USA",
"id":"usa",
"children":[
{
"title":"Northeast",
"id":"northeast"
},
{
"title":"Midwest",
"id":"midwest"
}
]
},
{
"title":"Europe",
"id":"europe"
}
]
}
]
}
]
What i want is when i pass "northeast" to a function() it should return me the dot notation string path from the root. Expected return string from the function in this case would be "links.world.usa.northeast"
You could test each nested array and if found, take the id from every level as path.
const pathTo = (array, target) => {
var result;
array.some(({ id, children = [] }) => {
if (id === target) return result = id;
var temp = pathTo(children, target)
if (temp) return result = id + '.' + temp;
});
return result;
};
var data = [{ title: "News", id: "news" }, { title: "Links", id: "links", children: [{ title: "World", id: "world", children: [{ title: "USA", id: "usa", children: [{ title: "Northeast", id: "northeast" }, { title: "Midwest", id: "midwest" }] }, { title: "Europe", id: "europe" }] }] }];
console.log(pathTo(data, 'northeast'));

Setting array keys dynamically based on length

Given an array in this format:
[
[{
name: "name",
value: "My-name"
},
{
name: "qty",
value: "1"
},
{
name: "url",
value: "test.com"
},
{
name: "comment",
value: "my-comment"
}
],
[{
name: "name",
value: "My-name2"
},
{
name: "qty",
value: "3"
},
{
name: "url",
value: "test2.com"
}
],
[{
name: "name",
value: "My-name3"
},
{
name: "qty",
value: "1"
},
{
name: "url",
value: "test3.com"
},
{
name: "comment",
value: "my-comment3"
}
]
]
I'm looking to switch that to:
[
[
{ name: "My-name" },
{ qty: "1" },
{ url: "test.com" },
{ comment: "my-comment", }
],[
{ name: "My-name2" },
{ qty: "3" },
{ url: "test2.com",
],[
{ name: "My-name3", },
{ qty: "1", },
{ url: "test3.com", },
{ comment: "my-comment3", }
]
]
In other words, swapping out the array keys but maintaining the object structure within each array element.
I've tried looping over each element and can swap the keys out using something like:
newArray[iCount][item.name] = item.value;
However I'm then struggling to preserve the object order. Note that the comment field may or may not appear in the object.
With Array.map() function:
var arr = [
[{name:"name",value:"My-name"},{name:"qty",value:"1"},{name:"url",value:"test.com"},{name:"comment",value:"my-comment"}],
[{name:"name",value:"My-name2"},{name:"qty",value:"3"},{name:"url",value:"test2.com"}],
[{name:"name",value:"My-name3"},{name:"qty",value:"1"},{name:"url",value:"test3.com"},{name:"comment",value:"my-comment3"}]
],
result = arr.map(function(a){
return a.map(function(obj){
var o = {};
o[obj.name] = obj.value
return o;
});
});
console.log(result);
Check my moreBetterOutput value. I think will be better.
If you still need a result like your example in the question then you can check output value.
const input = [
[
{
name:"name",
value:"My-name"
},
{
name:"qty",
value:"1"
},
{
name:"url",
value:"test.com"
},
{
name:"comment",
value:"my-comment"
}
],
[
{
name:"name",
value:"My-name2"
},
{
name:"qty",
value:"3"
},
{
name:"url",
value:"test2.com"
}
],
[
{
name:"name",
value:"My-name3"
},
{
name:"qty",
value:"1"
},
{
name:"url",
value:"test3.com"
},
{
name:"comment",
value:"my-comment3"
}
]
]
const output = input.map(arr => arr.map(obj => ({[obj.name]: obj.value})))
const moreBetterOutput = output.map(arr => arr.reduce((acc, item, index) => {
acc[Object.keys(item)[0]] = item[Object.keys(item)[0]];
return acc;
}, {}) )
//console.log(output);
console.log(moreBetterOutput);
Another map function:
const result = array.map( subarray =>
Object.assign(...subarray.map( ({name, value}) => ({ [name] : value }) ))
);

Categories

Resources