Get specific key depth in object with key value - javascript

const item = {
id: 'item1',
children: [
{ id: 'item1-1',
children: [
{ id: 'item1-1-1' },
{ id: 'item1-1-2' },
{ id: 'item1-1-3' },
]
},
{ id: 'item1-2',
children: [
{ id: 'item1-2-1' }
]
}
]
}
Like this,
function getLevelOfId(){
...
}
getLevelOfId('item1') =====> return 1
getLevelOfId('item1-2') =====> return 2
getLevelOfId('item1-1-1') =====> return 3
getLevelOfId('item1-1-2') =====> return 3
How to get specific object's depth with JavaScript?
Not use of id string. like ('item1-2').split('-').length Because each object has randomic id. Is there a simple way?

You need to iterate all objects and if found, take one for each level for the recursion depth.
function getLevelOfId(object, id) {
var level;
if (object.id === id) return 1;
object.children && object.children.some(o => level = getLevelOfId(o, id));
return level && level + 1;
}
const item = { id: 'item1', children: [{ id: 'item1-1', children: [{ id: 'item1-1-1' }, { id: 'item1-1-2' }, { id: 'item1-1-3' }] }, { id: 'item1-2', children: [{ id: 'item1-2-1' }] }] };
console.log(getLevelOfId(item, 'item1')); // 1
console.log(getLevelOfId(item, 'item1-2')); // 2
console.log(getLevelOfId(item, 'item1-1-1')); // 3
console.log(getLevelOfId(item, 'item1-1-2')); // 3
console.log(getLevelOfId(item, 'foo')); // undefined

if the structure id & children is fixed, how about search the whole value like "item1-1-1" in the json string:
{"id":"item1","children":[{"id":"item1-1","children":[{"id":"item1-1-1"},{"id":"item1-1-2"},{"id":"item1-1-3"}]},{"id":"item1-2","children":[{"id":"item1-2-1"}]}]}
level = (number of "{") - (number of "}") // before the searched positon of the string

Related

Remove duplicate elements of an array, in an object of arrays, dynamically

I have checked other solutions but none fit the criterion of my problem
This solution does not have the ability to dynamically check each node
Problem summarized
I wish to create an algorithm that is able to check an object that has nodes of different data types, for duplicated objects in nodes that are specifically of the datatype array.
I have the following dataset:
task = {
content: "lorem....",
customer: [
{ id: 1, name: "hello" },
{ id: 2, name: "sup" },
],
end: "2020-08-13 10:09:48",
project: [{ id: 1 }, { id: 1 }, { id: 2 }],
vendor: [{ id: 2 }, { id: 2 }, { id: 3 }],
};
I wish to be able to dynamically check which of the objects (or nodes? and the algo has to recognize that it is an array) has duplicates, and reduce them to be in this form:
task = {
content: "lorem....",
customer: [
{ id: 1, name: "hello" },
{ id: 2, name: "sup" },
],
end: "2020-08-13 10:09:48",
project: [{ id: 1 }, { id: 2 }],
vendor: [{ id: 2 }, { id: 3 }],
};
EDIT
The algorithm needs to be able to handle a dynamic number of nodes (example 1), however , the duplicates will only happen 1 level down (Thanks for pointing out).
example 1 (there is 1 less node here ) :
task = {
content: "lorem....",
customer: [
{ id: 1, name: "hello" },
{ id: 2, name: "sup" },
],
end: "2020-08-13 10:09:48",
project: [{ id: 1 }, { id: 2 }],
};
Here is my proposed solution to remove duplicate elements from any array in the task object:
const uniq = array => {
const map = {};
const result = [];
for (let i = 0; i < array.length; i++) {
// since elements can be objects, need to do a deep comparison.
const element = JSON.stringify(array[i]);
if (map[element] === undefined) {
map[element] = true;
result.push(array[i]);
}
}
return result;
}
const task = {
content: "lorem....",
customer: [
{ id: 1, name: "hello" },
{ id: 2, name: "sup" },
],
end: "2020-08-13 10:09:48",
project: [{ id: 1 }, { id: 1 }, { id: 2 }],
vendor: [{ id: 2 }, { id: 2 }, { id: 3 }],
};
for (const key in task) {
if (Array.isArray(task[key])) {
task[key] = uniq(task[key])
}
}
console.log('deduped:', task);

How to find an object from recursive array of objects that is matching with the id using javascript and react?

i have data like below,
const items = [
{
id: '1',
name: 'item1',
subItems: [{
id: '1',
name: 'subitem-1',
}],
},
{
id: '2',
name: 'item2',
subItems: [{
id: '2',
name: 'subitem-1',
}],
}
]
Now i want to find the subItem that matches with id 2. so from above data expected output is
{
id: '2',
name: 'subitem-1',
}
i have tried something like below,
const subItem = Items.find(
(item: any) =>
item.subItems &&
item.subItems.some(
(subItem: any) => subItem.id === '2'
)
);
but this will return the item that contains the subItem with id = '2'.
how can i fix the above code such that i get the SubItem instead of item?
could someone help me with this. thanks.
You could use the following, which will search and return only the inner object that is found.
const items = [
{
id: "1",
name: "item1",
subItems: [
{
id: "1",
name: "subitem-1"
}
]
},
{
id: "2",
name: "item2",
subItems: [
{
id: "2",
name: "subitem-1"
}
]
}
];
function innerFind(array, key, value) {
for (const obj of array) {
const item = obj.subItems.find((el) => el[key] === value);
if (item != null) return item;
}
}
console.log(innerFind(items, 'id', '2'));
You pass in your items array, the key you want to search for ('id'), and then the value you want to search for ('2'). This function will return the first sub item it finds that matches.

Building a tree recursively in JavaScript

I am trying to build a tree recursively from an array of objects. I am currently using the reduce() method to iterate through the items in the array and figure out which children belong to a particular item and populating it, then recursively populating the children of those children and so on. However, I have been unable to take the last nodes(e.g persian and siamese in this case) and put them in array(see expected and current output below)
let categories = [
{ id: 'animals', parent: null },
{ id: 'mammals', parent: 'animals' },
{ id: 'cats', parent: 'mammals' },
{ id: 'dogs', parent: 'mammals' },
{ id: 'chihuahua', parent: 'dogs' },
{ id: 'labrador', parent: 'dogs' },
{ id: 'persian', parent: 'cats' },
{ id: 'siamese', parent: 'cats' }
];
const reduceTree = (categories, parent = null) =>
categories.reduce(
(tree, currentItem) => {
if(currentItem.parent == parent){
tree[currentItem.id] = reduceTree(categories, currentItem.id);
}
return tree;
},
{}
)
console.log(JSON.stringify(reduceTree(categories), null, 1));
expected output:
{
"animals": {
"mammals": {
"cats": [ // <-- an array of cat strings
"persian",
"siamese"
],
"dogs": [ // <-- an array of dog strings
"chihuahua",
"labrador"
]
}
}
}
current output:
{
"animals": {
"mammals": {
"cats": { // <-- an object with cat keys
"persian": {},
"siamese": {}
},
"dogs": { // <-- an object with dog keys
"chihuahua": {},
"labrador": {}
}
}
}
}
How should I go about solving the problem?
I put a condition to merge the result as an array if a node has no child. Try this
let categories = [
{ id: 'animals', parent: null },
{ id: 'mammals', parent: 'animals' },
{ id: 'cats', parent: 'mammals' },
{ id: 'dogs', parent: 'mammals' },
{ id: 'chihuahua', parent: 'dogs' },
{ id: 'labrador', parent: 'dogs' },
{ id: 'persian', parent: 'cats' },
{ id: 'siamese', parent: 'cats' }
];
const reduceTree = (categories, parent = null) =>
categories.reduce(
(tree, currentItem) => {
if(currentItem.parent == parent){
let val = reduceTree(categories, currentItem.id);
if( Object.keys(val).length == 0){
Object.keys(tree).length == 0 ? tree = [currentItem.id] : tree.push(currentItem.id);
}
else{
tree[currentItem.id] = val;
}
}
return tree;
},
{}
)
console.log(JSON.stringify(reduceTree(categories), null, 1));
NOTE: if your data structure changes again this parser might fail for some other scenarios.
Here is a solution without recursion:
const categories = [{ id: 'animals', parent: null },{ id: 'mammals', parent: 'animals' },{ id: 'cats', parent: 'mammals' },{ id: 'dogs', parent: 'mammals' },{ id: 'chihuahua', parent: 'dogs' },{ id: 'labrador', parent: 'dogs' },{ id: 'persian', parent: 'cats' },{ id: 'siamese', parent: 'cats' }];
// Create properties for the parents (their values are empty objects)
let res = Object.fromEntries(categories.map(({parent}) => [parent, {}]));
// Overwrite the properties for the parents of leaves to become arrays
categories.forEach(({id, parent}) => res[id] || (res[parent] = []));
// assign children to parent property, either as property of parent object or as array entry in it
categories.forEach(({id, parent}) => res[parent][res[id] ? id : res[parent].length] = res[id] || id);
// Extract the tree for the null-entry:
res = res.null;
console.log(res);

Filter nested object from parent object

I have a parent object with a child array of objects nest underneath. Each object contains an id key with a unique value. A filter function needs to search the parent object for an id, if it does not equal the given id then recursively search through nested objects for the id until it is found. Once the object with the given key is found the remove and return the updated myObject.
The structure looks as followed:
let myObject = {
key: 1,
name: 'hello',
children: [
{
key: 2,
name: 'world',
children: []
},
{
key: 3,
name: 'hope',
children: [
{
key: 4,
name: 'you',
children: [{
key: 5,
name: 'are',
children: []
}]
},
{
key: 6,
name: 'having',
children: [{
key: 7,
name: 'fun',
children: []
}]
}
]
}
]
}
let given = 4;
if (myObject.key !== given) {
myObject = searchChild(myObject, given)
} else {
myObject = {}
}
function searchChild(parent, given) {
parent.children.map(child => {
return child.children.filter(item => {
if (item.key === given) return item;
else if (item.key !== given
&& child.children.length > 0
&& child.children != undefined) {
searchChild(child.children, given);
}
})
})
}
Currently, I am receiving a type error when running the recursive function.
The output should look like where the keys are updated to the new order in tree:
{
key: 1,
name: 'hello',
children: [
{
key: 2,
name: 'world',
children: []
},
{
key: 3,
name: 'hope',
children: [
{
key: 4,
name: 'having',
children: [{
key: 5,
name: 'fun',
children: []
}]
}
]
}
]
}
Here is function you can call for your object
function searchInChild(parent,key){
parent.children = parent.children.filter((c)=>{
if(key == c.key ){
result = c;
return false;
}
return true;
});
if(result == null){
for(c in parent.children){
searchInChild(parent.children[c],key);
}
}
}
Where, you can simply pass searchInChild(myObject,key) & make result global variable.
You pass child.children but you have to pass child you already iterate through children in the function.
let myObject = {
key: 1,
name: 'hello',
children: [
{
key: 2,
name: 'world',
children: []
},
{
key: 3,
name: 'hope',
children: [
{
key: 4,
name: 'you',
children: [{
key: 5,
name: 'are',
children: []
}]
},
{
key: 6,
name: 'having',
children: [{
key: 7,
name: 'fun',
children: []
}]
}
]
}
]
}
let given = 4;
if (myObject.key !== given) {
myObject = searchChild(myObject, given)
} else {
myObject = {}
}
function searchChild(parent, given) {
if(parent && parent.children) {
parent.children.map(child => {
return child.children.filter(item => {
if (item.key === given) return item;
else if (item.key !== given
&& child.children.length > 0
&& child.children != undefined) {
searchChild(child, given);
}
})
})
}
}

filter nested tree object without losing structure

I have nested tree object I would like filter through without losing structure
var items = [
{
name: "a1",
id: 1,
children: [{
name: "a2",
id: 2,
children: [{
name: "a3",
id: 3
}]
}]
}
];
so for example if id == 2 remove object with id 2 and his children
if id == 3 only remove object with id 3
this's just apiece of object to make question clean but the object it self contains more and more :)
using vanilla javascript, _lodash or Angular2 it's okay
thank you
You can create recursive function using filter() and also continue filtering children if value is Array.
var items = [{
name: "a1",
id: 1,
children: [{
name: "a2",
id: 2,
children: [{
name: "a3",
id: 3
}, ]
}]
}];
function filterData(data, id) {
var r = data.filter(function(o) {
Object.keys(o).forEach(function(e) {
if (Array.isArray(o[e])) o[e] = filterData(o[e], id);
})
return o.id != id
})
return r;
}
console.log(filterData(items, 3))
console.log(filterData(items, 2))
Update: As Nina said if you know that children is property with array you don't need to loop keys you can directly target children property.
var items = [{
name: "a1",
id: 1,
children: [{
name: "a2",
id: 2,
children: [{
name: "a3",
id: 3
}, ]
}]
}];
const filterData = (data, id) => data.filter(o => {
if (o.children) o.children = filterData(o.children, id);
return o.id != id
})
console.log(JSON.stringify(filterData(items, 3), 0, 2))
console.log(JSON.stringify(filterData(items, 2), 0, 2))
If it's ok for your case to use Lodash+Deepdash, then:
let filtered = _.filterDeep(items,(i)=>i.id!=3,{tree:true});
Here is a demo Codepen
You could use an iterative approach with Array#some and call the callback iter recursive for the children. I found, splice.
function deleteItem(id) {
items.some(function iter(a, i, aa) {
if (a.id === id) {
aa.splice(i, 1);
return true;
}
return a.children.some(iter);
});
}
var items = [{ name: "a1", id: 1, children: [{ name: "a2", id: 2, children: [{ name: "a3", id: 3 }] }] }];
console.log(items);
deleteItem(3);
console.log(items);
deleteItem(2);
console.log(items);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Use recursive function:
var items = [
{
name: "a1",
id: 1,
children: [{
name: "a2",
id: 2,
children: [{
name: "a3",
id: 3,
children: [{
name: "a4",
id: 4,
}]
}]
}]
}
];
function filterId(items, id) {
var len = items.length;
while (len--) {
if (items[len].id === id) {
items.splice(len, 1);
break;
} else {
filterId(items[len].children, id);
}
}
return items;
}
// filtering out the item with 'id' = 4
console.log(filterId(items, 4));
// filtering out the item with 'id' = 2
console.log(filterId(items, 2));

Categories

Resources