How to convert json to tree array in JS? - javascript

I would like to convert this json / object to this specific structure below to allow me to use a treeList component.
I've tried to build a recursive function but I didn't find the solution yet.
Thanks for your help
const data = {
parent1: {
child1: { bar: "1" },
child2: "2"
},
parent2: {
child1: "1"
}
}
to
const treeData = [
{
title: "parent1",
key: "parent1",
children: [
{
title: "child1",
key: "child1",
children: [{ title: "bar", key: "bar", value: "1" }]
},
{
title: "child2",
key: "child2",
value: "2"
}
],
},
{
title: "parent2",
key: "parent2",
children: [
{
title: "child1",
key: "child1",
value: "1"
}
]
}
]

You could take an iterative and recursive approach.
function getNodes(object) {
return Object
.entries(object)
.map(([key, value]) => value && typeof value === 'object'
? { title: key, key, children: getNodes(value) }
: { title: key, key, value }
);
}
const data = { parent1: { child1: { bar: "1" }, child2: "2" }, parent2: { child1: "1" } },
result = getNodes(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

just share sample, a little different from yours. But it give you a hint with recursive function.
https://jsfiddle.net/segansoft/7bdxmys4/1/
function getNestedChildren(arr, parent) {
var out = []
for (var i in arr) {
if (arr[i].parent == parent) {
var children = getNestedChildren(arr, arr[i].id)
if (children.length) {
arr[i].children = children
}
out.push(arr[i])
}
}
return out
}
var flat = [{
id: 1,
title: 'hello',
parent: 0
},
{
id: 2,
title: 'hello',
parent: 0
},
{
id: 3,
title: 'hello',
parent: 1
},
{
id: 4,
title: 'hello',
parent: 3
},
{
id: 5,
title: 'hello',
parent: 4
},
{
id: 6,
title: 'hello',
parent: 4
},
{
id: 7,
title: 'hello',
parent: 3
},
{
id: 8,
title: 'hello',
parent: 2
}
]
var nested = getNestedChildren(flat, 0)
document.write('<pre>' + JSON.stringify(nested, 0, 4) + '</pre>');

Related

Filter 2 arrays to check if parent child

I have first array -
let parent = [
{
id:1,
value:"ABC",
},
{
id:2,
value:"DEF",
},
{
id:3,
value:"GHI",
},
{
id:4,
value:"JKL",
},
{
id:5,
value:"MNO",
},
{
id:6,
value:"PQR",
},
]
And 2nd Array Object -
let child = [
{
childid:1,
value:"ABC",
},
{
childid:2,
value:"DEF",
},
{
childid:10,
value:"GHI",
},
]
From parent array I want to select all those elements whose id matches with childid from child array.
I tried -
parent.filter(x=>x.id==child.each(y=>y.childid))
But its not working
You can use some() to do it
let parent = [
{
id:1,
value:"ABC",
},
{
id:2,
value:"DEF",
},
{
id:3,
value:"GHI",
},
{
id:4,
value:"JKL",
},
{
id:5,
value:"MNO",
},
{
id:6,
value:"PQR",
},
]
let child = [
{
childid:1,
value:"ABC",
},
{
childid:2,
value:"DEF",
},
{
childid:10,
value:"GHI",
},
]
let result = parent.filter(p => child.some(a => a.childid == p.id ))
console.log(result)
using Flatmap and filter ...
let parent = [{
id: 1,
value: "ABC",
},
{
id: 2,
value: "DEF",
},
{
id: 3,
value: "GHI",
},
{
id: 4,
value: "JKL",
},
{
id: 5,
value: "MNO",
},
{
id: 6,
value: "PQR",
},
]
let child = [{
childid: 1,
value: "ABC",
},
{
childid: 2,
value: "DEF",
},
{
childid: 10,
value: "GHI",
},
]
const res = parent.flatMap(x => child.filter(y => y.childid === x.id))
console.log(res)
This would work
parent.filter(p => child.some(c => c.childid === p.id))
Wat happens is
For each element in parent array, find the corresponding element in the child array
If it exists the filter will see it as truthy and keep the parent element, if not it will be falsy and filter wil discard it
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
const filterResult = parent.filter(x => child.some(y => y.childid == x.id))
You can use a reduce function along with a forEach to loop through the child elements and compare against the parent.
const result = parents.reduce((acc, parent) => {
children.forEach((child) => {
if (parent.id === child.childid) {
acc.push(parent);
}
});
return acc;
}, []);
console.log(result); // [{"id":1,"value":"ABC"},{"id":2,"value":"DEF"}]
const parents = [{
id: 1,
value: 'ABC',
},
{
id: 2,
value: 'DEF',
},
{
id: 3,
value: 'GHI',
},
{
id: 4,
value: 'JKL',
},
{
id: 5,
value: 'MNO',
},
{
id: 6,
value: 'PQR',
},
];
const children = [{
childid: 1,
value: 'ABC',
},
{
childid: 2,
value: 'DEF',
},
{
childid: 10,
value: 'GHI',
},
];
const result = parents.reduce((acc, parent) => {
children.forEach((child) => {
if (parent.id === child.childid) {
acc.push(parent);
}
return acc;
});
return acc;
}, []);
console.log(result);
MDN Reduce

How to remove Children Nodes and add them to parent Node by removing current Node?

I'm trying to organize a JSON data. Here is my JSON:
let all = [
{
id: "n1",
name: "Hipokrat",
children: [
{
id: "n2",
name: "Edward Janner",
children: [
{
id: "n9",
name: "Edison, Thomas",
}
]
}
]
},
{
id: "n3",
name: "William Harvey",
children: [
{
id: "n10",
name: "Lister, Joseph",
children: [
{
id: "n11",
name: "Kant, Immanuel",
children: [
{
id: "n15",
name: "Rawls, John"
},
{
id: "n46",
name: "More, Thomas",
},
{
id: "n47",
name: "Galen",
}
]
}
]
},
{
id: "n12",
name: "Smith, Adam",
}
]
},
{
id: "n48",
name: "Osler, William",
children: [
{
id: "n51",
name: "Louis Pasteur",
}
]
},
{
id: "n52",
name: "John Hunter",
children: [
{
id: "n53",
name: "Freud, Sigmund",
}
]
}
];
At here, I want to find the node with "n10" id and move them to the ancestor node which is "William Harvey" and with "n3" id. We should add these to children array.
So I want this result:
let all = [
{
id: "n1",
name: "Hipokrat",
children: [
{
id: "n2",
name: "Edward Janner",
children: [
{
id: "n9",
name: "Edison, Thomas",
}
]
}
]
},
{
id: "n3",
name: "William Harvey",
children: [
{
id: "n11",
name: "Kant, Immanuel",
children: [
{
id: "n15",
name: "Rawls, John"
},
{
id: "n46",
name: "More, Thomas",
},
{
id: "n47",
name: "Galen",
}
]
},
{
id: "n12",
name: "Smith, Adam",
}
]
},
{
id: "n48",
name: "Osler, William",
children: [
{
id: "n51",
name: "Louis Pasteur",
}
]
},
{
id: "n52",
name: "John Hunter",
children: [
{
id: "n53",
name: "Freud, Sigmund",
}
]
}
];
Here is my try:
let all = [
{
id: "n1",
name: "Hipokrat",
children: [
{
id: "n2",
name: "Edward Janner",
children: [
{
id: "n9",
name: "Edison, Thomas",
}
]
}
]
},
{
id: "n3",
name: "William Harvey",
children: [
{
id: "n11",
name: "Kant, Immanuel",
children: [
{
id: "n15",
name: "Rawls, John"
},
{
id: "n46",
name: "More, Thomas",
},
{
id: "n47",
name: "Galen",
}
]
},
{
id: "n12",
name: "Smith, Adam",
}
]
},
{
id: "n48",
name: "Osler, William",
children: [
{
id: "n51",
name: "Louis Pasteur",
}
]
},
{
id: "n52",
name: "John Hunter",
children: [
{
id: "n53",
name: "Freud, Sigmund",
}
]
}
];
function isObject(variable){
if ( typeof variable === 'object' && !Array.isArray(variable) && variable !== null) {
return true;
} else{
return false;
}
}
function isArray(variable){
if(!isObject(variable) && Array.isArray(variable)){
return true;
} else{
return false;
}
}
function process(all, value, indexes = "", foundAndChildren = false) {
if(foundAndChildren){
return foundAndChildren;
}
if(isArray(all)){ // is Array
console.log("-> Children size: " + all.length);
console.log("-PASSED WITH 1");
console.log("-----------------");
console.log("");
all.forEach(subElement => {
return process(subElement, value);
});
} else if(isObject(all)){ // is object
if(all["id"] == value){ // Match
console.log("#########");
console.log(all.id);
console.log(all.name);
console.log("--PASSED WITH 2");
let children = [];
if(all["children"] != undefined){
children = all.children
}
let res = {
id: value,
indexes: indexes,
children: children
};
console.log(res);
return res;
} else {
console.log(all.name);
console.log("--PASSED WITH 3");
console.log("");
if(isArray(all["children"])) { // is object but has children
console.log("->" + all.name);
console.log("Children size:" + all.children.length);
console.log("--PASSED WITH 4");
console.log("");
all["children"].forEach(elementOfChildren => {
return process(elementOfChildren, value);
});
}
}
}
}
console.log(process(all, "n10"));
And here is the playground: https://playcode.io/823402/
You can do something like this:
function replaceNode(jsonData, id) {
function replaceChildren(o, id) {
for (let key in o) {
const object = o[key]
if (object.id == id) {
if (object.children && object.children.length > 0) {
o.splice(key, 1, ...object.children)
return true
} else throw `id: ${id} has no children`
} else {
if (object.children && object.children.length > 0) {
const found = replaceChildren(object.children, id)
if (found) return found
}
}
}
}
const o = [...jsonData]
replaceChildren(o, id)
return o
}
and then pass the json data and the node id
const output = replaceNode(all, 'n10')

Flattening 1 level of object array and appending parent object

I have an object like which I want to flatten:
const obj = [
{
id: 1,
name: 'parent1',
children: [
{
childName: ''child11"
}
]
},
{
id: 2,
name: 'parent2',
children: [
{
childName: ''child21"
},
{
childName: ''child22"
}
]
}
]
I want my object to be like below:
eObj = [
{
p_id: 1,
p_name: "parent1",
childName: "child11"
},
{
p_id: 2,
p_name: "parent2",
childName: "child21"
},
{
p_id: 2,
p_name: "parent2",
childName: "child22"
}
]
I have done it using:
const eObj = [];
obj.forEach((o) => {
o.children.forEach((co) => {
eObj.push({
p_id: o.id,
p_name: o.name,
...children,
});
});
});
Is there a better way to do it, and also to reduce the nested for loop? I would really appreciate it.
If you are interested in what solution is more efficient.
Turns out your double forEach-solution is actually faster than the flatMap & map.
flatMap & map
const obj = [{
id: 1,
name: "parent1",
children: [{
childName: "child11",
}, ],
},
{
id: 2,
name: "parent2",
children: [{
childName: "child21",
},
{
childName: "child22",
},
],
},
];
const result = obj.flatMap(({
children,
id,
name
}) =>
children.map((child) => ({
p_id: id,
p_name: name,
childName: child.childName,
}))
);
console.log(result);
double forEach
const obj = [{
id: 1,
name: "parent1",
children: [{
childName: "child11",
}, ],
},
{
id: 2,
name: "parent2",
children: [{
childName: "child21",
},
{
childName: "child22",
},
],
},
];
const result = [];
obj.forEach((o) => {
o.children.forEach((co) => {
result.push({
p_id: o.id,
p_name: o.name,
childName: co.childName,
});
});
});
console.log(result);
You can use flatmap. This works:
eObj = obj.flatMap(item => item.children.map(child => ({p_id: item.id, p_name: item.name, childName: child.childName})))
Each way to do that needs two levels loop. You may use map or reduce function and your code may be clear but nothing change the time complexity.
But in this scenario you only need map function:
const obj = [
{
id: 1,
name: 'parent1',
children: [
{
childName: 'child11'
}
]
},
{
id: 2,
name: 'parent2',
children: [
{
childName: 'child21'
},
{
childName: 'child22'
}
]
}
]
const eObj = obj.flatMap(t=>t.children.map(c => { return {p_id: t.id,p_name: t.name, childName: c.childName}}))
console.log(eObj);

Nested array filtering, is there a more elegant way?

I'm trying to filter a nested structure, based on a search string.
If the search string is matched in an item, then I want to keep that item in the structure, along with its parents.
If the search string is not found, and the item has no children, it can be discounted.
I've got some code working which uses a recursive array filter to check the children of each item:
const data = {
id: '0.1',
children: [
{
children: [],
id: '1.1'
},
{
id: '1.2',
children: [
{
children: [],
id: '2.1'
},
{
id: '2.2',
children: [
{
id: '3.1',
children: []
},
{
id: '3.2',
children: []
},
{
id: '3.3',
children: []
}
]
},
{
children: [],
id: '2.3'
}
]
}
]
};
const searchString = '3.3';
const filterChildren = (item) => {
if (item.children.length) {
item.children = item.children.filter(filterChildren);
return item.children.length;
}
return item.id.includes(searchString);
};
data.children = data.children.filter(filterChildren);
console.log(data);
/*This outputs:
{
"id": "0.1",
"children": [
{
"id": "1.2",
"children": [
{
"id": "2.2",
"children": [
{
"id": "3.3",
"children": []
}
]
}
]
}
]
}*/
I'm concerned that if my data structure becomes massive, this won't be very efficient.
Can this be achieved in a 'nicer' way, that limits the amount of looping going on? I'm thinking probably using a reducer/transducer or something similarly exciting :)
A nonmutating version with a search for a child.
function find(array, id) {
var child,
result = array.find(o => o.id === id || (child = find(o.children, id)));
return child
? Object.assign({}, result, { children: [child] })
: result;
}
const
data = { id: '0.1', children: [{ children: [], id: '1.1' }, { id: '1.2', children: [{ children: [], id: '2.1' }, { id: '2.2', children: [{ id: '3.1', children: [] }, { id: '3.2', children: [] }, { id: '3.3', children: [] }] }, { children: [], id: '2.3' }] }] },
searchString = '3.3',
result = find([data], searchString);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Join two objects by key

I stuck on mergin 2 objects into one. Let's say I have 2 arrays of objects:
One is childs:
let childsWithMoreInfo = [{
id: 1,
name: 'somename',
parent: {
id: 2
},
}, {
id: 2,
name: 'some child name',
parent: {
id: 4
}
}];
And the second one is Parents:
let parents = [{
id: 1,
parentName: 'The first',
child: {}
}, {
id: 2,
parentName: 'The second',
child: {}
}, {
id: 3,
parentName: 'The third',
child: {}
}, {
id: 4,
parentName: 'The fourth',
child: {}
}];
And I would to merge these objects like this:
let combined = [
{
id: 1,
parentName: The first,
child: {}
},
{
id: 2,
parentName: The second,
child: {
id: 1,
name: somename,
}
},
{
id: 3,
parentName: The third,
child: {}
},
{
id: 4,
parentName: The fourth,
child: {
id: 2
name: some child name,
}
},
]
];
So basically it should be something like:
let combinedList = parents.child = childsWithMoreInfo where parents.id = childsWithMoreInfo.parent.id . On which method I should take a look? Do you have any ideas how can easily achieve that?
I really know how to use forEach, I wanted to avoid it.
This is what I made:
this.combined = _.map(parents, (parent) => {
parent.child = childs.find(child => child.parent.id === parent.id);
return parent;
});
Thank you for all of your answers.
You could use a Map and iterate then with Array#forEach for every object.
Then have a lookup in the map and assign the values to ch parent object.
var childsWithMoreInfo = [{ id: 1, name: 'somename', parent: { id: 2 } }, { id: 2, name: 'some child name', parent: { id: 4 } }],
parents = [{ id: 1, parentName: 'The first', child: {} }, { id: 2, parentName: 'The second', child: {} }, { id: 3, parentName: 'The third', child: {} }, { id: 4, parentName: 'The fourth', child: {} }],
map = new Map;
parents.forEach(p => map.set(p.id, p));
childsWithMoreInfo.forEach(c => {
var o = map.get(c.parent.id);
o.child = { id: c.id, name: c.name };
});
console.log(parents);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Another solution would be the use of Array#find
var childsWithMoreInfo = [{ id: 1, name: 'somename', parent: { id: 2 } }, { id: 2, name: 'some child name', parent: { id: 4 } }],
parents = [{ id: 1, parentName: 'The first', child: {} }, { id: 2, parentName: 'The second', child: {} }, { id: 3, parentName: 'The third', child: {} }, { id: 4, parentName: 'The fourth', child: {} }];
childsWithMoreInfo.forEach(c => {
var o = parents.find(p => p.id === c.parent.id);
o.child = { id: c.id, name: c.name };
});
console.log(parents);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Following code has some pure functions to do your task. I have formatted/cleaned the input objects also:
'use strict';
let childsWithMoreInfo = [{
id: 1,
name: 'somename',
parent: {
id: 2
},
}, {
id: 2,
name: 'some child name',
parent: {
id: 4
}
}];
let parents = [{
id: 1,
parentName: 'The first',
child: {}
}, {
id: 2,
parentName: 'The second',
child: {}
}, {
id: 3,
parentName: 'The third',
child: {}
}, {
id: 4,
parentName: 'The fourth',
child: {}
}];
function makeObjectFromArray(arr) {
let obj = {};
arr.map(function(item) {
if (obj[item.id] === undefined) {
obj[item.id] = item
}
})
return obj;
}
function toArray(obj) {
return Object.keys(obj).map(function(key) {
return obj[key]
});
}
function sampleParentChildren(parent, children) {
let Parent = {};
if (parent.constructor === Array) {
Parent = makeObjectFromArray(parent);
} else {
Parent = Object.assign({}, parent)
}
children.map(function(child) {
if (Parent[child.parent.id] !== undefined) {
if (Parent[child.parent.id].child === undefined) {
Parent[child.parent.id].child = {};
}
Parent[child.parent.id].child[child.id] = child
}
});
return Parent;
}
let resampledData = sampleParentChildren(parents, childsWithMoreInfo);
console.log(resampledData, toArray(resampledData));
To join the arrays in ES6, you can use the spread operator to join the arrays and just use plain old forEach to deduping. The spread operator is more declarative than Es5's array concatenation methods as mentioned here on MDN
[...parents, ...childsWithMoreInfo]
Here's a working example:
let childsWithMoreInfo = [{
id: 1,
name: 'somename',
parent: {
id: 2
},
}, {
id: 2,
name: 'some child name',
parent: {
id: 4
}
}],
parents = [{
id: 1,
parentName: 'The first',
child: {}
}, {
id: 2,
parentName: 'The second',
child: {}
}, {
id: 3,
parentName: 'The third',
child: {}
}, {
id: 4,
parentName: 'The fourth',
child: {}
},
];
var conjoined = [...parents, ...childsWithMoreInfo];
conjoined.forEach(function(parentConjoinee, parentIndex) {
conjoined.forEach(function(childConjoinee, childIndex) {
if (parentConjoinee.id === childConjoinee.id && parentIndex !== childIndex) {
conjoined.splice(childIndex, 1);
}
});
});
console.log(conjoined);
You can use nested for loops, break
var childsWithMoreInfo = [{
id: 1,
name: "somename",
parent: {
id: 2
}
}, {
id: 2,
name: "some child name",
parent: {
id: 4
}
}];
var parents = [{
id: 1,
parentName: "The first",
child: {}
}, {
id: 2,
parentName: "The second",
child: {}
}, {
id: 3,
parentName: "The third",
child: {}
}, {
id: 4,
parentName: "The fourth",
child: {}
}
];
let combined = [];
for (var i = 0; i < parents.length; i++) {
for (var j = 0; j < childsWithMoreInfo.length; j++) {
if (childsWithMoreInfo[j].parent.id === parents[i].id) {
parents[i].child.id = childsWithMoreInfo[j].id;
parents[i].child.name = childsWithMoreInfo[j].name;
break;
}
}
combined.push(parents[i])
};
console.log(combined);

Categories

Resources