How to get nested parent path from a JSON tree in JavaScript? - 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'));

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);

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}

Advanced filter object in js

I'm trying filter Object of Arrays with Object but i don't have idea what can I do it.
Sample:
{
245: [
{
id: "12",
name: "test",
status: "new"
},
{
id: "15",
name: "test2",
status: "old"
},
{
id: "12",
name: "test2",
status: "old"
}],
2815: [
{
id: "19",
name: "test",
status: "new"
},
{
id: "50",
name: "test2",
status: "old"
},
{
id: "120",
name: "test2",
status: "new"
}]
}
Need filter if status = "new" but struct must not change:
{
245: [{
id: "12",
name: "test",
status: "new"
}],
2815: [{
id: "19",
name: "test",
status: "new"
},
{
id: "120",
name: "test2",
status: "new"
}]
}
Loop over entries and create a new object with filtered values
const obj = {
245:[
{id:"12",name:"test",status:"new"},{id:"15",name:"test2",status:"old"},{id:"12",name:"test2",status:"old"}],
2815:[
{id:"19",name:"test",status:"new"},{id:"50",name:"test2",status:"old"},{id:"120",name:"test2",status:"new"}]
}
console.log(filter(obj, item => item.status === "new"))
function filter(obj, pred) {
return Object.fromEntries(Object.entries(obj).map(([name, value]) => [name, value.filter(pred)]))
}
You need to map over the object keys and then over the array elements to filter out the final result
var obj = {
245:[
{id:"12",name:"test",status:"new"},{id:"15",name:"test2",status:"old"},{id:"12",name:"test2",status:"old"}],
2815:[
{id:"19",name:"test",status:"new"},{id:"50",name:"test2",status:"old"},{id:"120",name:"test2",status:"new"}]
}
var res = Object.entries(obj).reduce((acc, [key, value]) => {
acc[key] = value.filter(item => item.status === "new");
return acc;
}, {})
console.log(res);
you can do it for this specific case like this:
const myObj = {
245:[
{id:"12",name:"test",status:"new"},
{id:"15",name:"test2",status:"old"},
{id:"12",name:"test2",status:"old"}
],
2815:[
{id:"19",name:"test",status:"new"},
{id:"50",name:"test2",status:"old"},
{id:"120",name:"test2",status:"new"}
]
};
const filteredObj = filterMyObj(myObj);
console.log(filteredObj);
function filterMyObj(myObj){
const myObjCopy = {...myObj};
for (const key in myObjCopy){
const myArrCopy = [...myObjCopy[key]];
myObjCopy[key] = myArrCopy.filter(item => item.status == "new");
}
return myObjCopy;
}
You can do it with filter :
for(let key in obj){
obj[key] = obj[key].filter(el => el.status == "new")
}

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; }

tree search using recursion javascript

I am looking for a way to be able to search in an array, with nested arrays, a node with information. It can be seen as a tree
const data = [
{
id: '1-1',
name: "Factory",
children: [
{
id: '1-1-1',
name: "Areas",
children: [
{
id: '1-1-1-1',
name: "Sales",
children: [
{
id: '1-1-1-1-1',
name: "Bill Gates",
children:[...]
},
...
]
},
...
]
},
...
],
},
...
]
If I wanted to find the node with name: Bill Gates
Try this function, but it does not work properly
const getElements = (treeData, text) => {
return treeData.map(node => {
const textMatch = node.name.toLowerCase().includes(text.toLowerCase());
if (textMatch) {
console.log(node);
return node;
} else {
if (node.children) {
return getElements(node.children, text)
}
}
})
}
In deeper data like Bill Gates Node returns the entire TreeArray, but with all the data that does not contain the name Bill Gates as undefined
You probably don't want to use .map here, because you don't want a mutated array, you just want to find a node. Using a for loop gets the expected result:
const data = [{
id: '1-1',
name: "Factory",
children: [
{
id: '1-1-1',
name: "Areas",
children: [
{
id: '1-1-1-1',
name: "Sales",
children: [
{
id: '1-1-1-1-1',
name: "Bill Gates",
children:[]
},
]
},
]
},
]
}];
const getElements = (treeData, text) => {
for (let i=0, node = treeData[i]; node; i++) {
const textMatch = node.name.toLowerCase().includes(text.toLowerCase());
if (textMatch) {
console.log(node);
return node;
} else if (node.children) {
return getElements(node.children, text)
}
}
};
getElements(data, 'Bill Gates');

Categories

Resources