I have data that is structured like the following:
const arr = [{
id: 0,
name: 'Biomes',
icon: 'mdi-image-filter-hdr',
isParent: true,
children: [{
id: 1,
name: 'Redwood forest',
icon: 'mdi-image-filter-hdr'
}]
},{
id: 2,
name: 'Trees',
icon: 'mdi-pine-tree',
children: [{
id: 8,
name: 'Redwood',
icon: 'mdi-pine-tree'
}]
}];
The top level is an array of objects, and each object can have a child field children which itself can be an array of objects. If I know the value of id, how can I find the object (or more importantly the object name), based on that id value?
const arr = [{
id: 0,
name: 'Biomes',
icon: 'mdi-image-filter-hdr',
isParent: true,
children: [{
id: 1,
name: 'Redwood forest',
icon: 'mdi-image-filter-hdr'
}]
},{
id: 2,
name: 'Trees',
icon: 'mdi-pine-tree',
children: [{
id: 0,
name: 'whatever',
icon: 'new-tree'
},{
id: 8,
name: 'Redwood',
icon: 'mdi-pine-tree'
}]
}];
const findById = (id) => arr
.filter(x => x.id === id || x.children.some(child => id === child.id))
.map(y => ({...y, children: y.children.filter(child => id === child.id)}))
console.log(findById(0))
You can first filter out all parents who has the same id or if the parent has child with same id via some, which then using map to and filter to remove all children who don't have the id
You can use recursion here to get the object no matter how deep it is. I've filtered out the object. It will only give the object without children property. You can include it in the way you want
function getObjectFromId(arr, id) {
for (let i = 0; i < arr.length; ++i) {
if (arr[i].id === id) {
// return arr[i] // If you want full object with children
const { children, ...rest } = arr[i];
return rest;
}
if (arr[i].children) {
const result = getObjectFromId(arr[i].children, id);
if (result) return result;
}
}
}
const arr = [
{
id: 0,
name: "Biomes",
icon: "mdi-image-filter-hdr",
isParent: true,
children: [
{
id: 1,
name: "Redwood forest",
icon: "mdi-image-filter-hdr",
},
],
},
{
id: 2,
name: "Trees",
icon: "mdi-pine-tree",
children: [
{
id: 8,
name: "Redwood",
icon: "mdi-pine-tree",
children: [
{
id: 9,
name: "Redwood",
icon: "mdi-pine-tree",
},
],
},
],
},
];
function getObjectFromId(arr, id) {
for (let i = 0; i < arr.length; ++i) {
if (arr[i].id === id) {
// return arr[i] // If you want full object with children
const { children, ...rest } = arr[i];
return rest;
}
if (arr[i].children) {
const result = getObjectFromId(arr[i].children, id);
if (result) return result;
}
}
}
console.log(getObjectFromId(arr, 1));
console.log(getObjectFromId(arr, 8));
console.log(getObjectFromId(arr, 9));
/* This is not a part of answer. It is just to give the output fill height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }
Solution 1: 2 for loop.
const inputArray = [{
id: 0,
name: 'Biomes',
icon: 'mdi-image-filter-hdr',
isParent: true,
children: [{
id: 1,
name: "Redwood forest",
icon: 'mdi-image-filter-hdr'
}]
},
{
id: 2,
name: 'Trees',
icon: 'mdi-pine-tree',
children: [{
id: 8,
name: "Redwood",
icon: 'mdi-pine-tree',
}]
}
];
console.log(findById(8));
function findById(id) {
for (const parent of inputArray) {
if (parent.id === id)
return parent;
for (const child of parent.children)
if (child.id === id)
return child;
}
return null;
}
Solution 2: multiple nested level:
const inputArray = [{
id: 0,
name: 'Biomes',
icon: 'mdi-image-filter-hdr',
isParent: true,
children: [{
id: 1,
name: "Redwood forest",
icon: 'mdi-image-filter-hdr'
}]
},
{
id: 2,
name: 'Trees',
icon: 'mdi-pine-tree',
children: [{
id: 8,
name: "Redwood",
icon: 'mdi-pine-tree',
children: [{
id: 12,
name: "Test Level 2",
icon: 'mdi-pine-tree',
children: [{
id: 13,
name: "Test Level 3",
icon: 'mdi-pine-tree',
}]
}]
}]
}
];
console.log(findById(13, inputArray));
function findById(id, array) {
for (const parent of array) {
if (parent.id === id)
return parent;
if ('children' in parent) {
const result = findById(id, parent.children);
if (result) return result;
}
}
return null;
}
To find at parent and child level
const arr=[{id:0,name:'Biomes',icon:'mdi-image-filter-hdr',isParent:!0,children:[{id:1,name:"Redwood forest",icon:'mdi-image-filter-hdr'}]},{id:2,name:'Trees',icon:'mdi-pine-tree',children:[{id:8,name:"Redwood",icon:'mdi-pine-tree',}]}]
const res = arr.reduce((a,item)=>{
const { children, ...i} = item;
a.push(...children, i);
return a;
},[]);
function findInData(id){
return res.find(o=>o.id===id);
}
console.log(findInData(8)?.name);
console.log(findInData(2)?.name);
First arg for child
Second arg for main
const arr = [
{id: 0,name: 'Biomes',icon: 'mdi-image-filter-hdr',isParent: true,
children: [{id: 1,name: 'Redwood forest',icon: 'mdi-image-filter-hdr',},],},
{id: 2,name: 'Trees',icon: 'mdi-pine-tree',
children: [{id: 8,name: 'Redwood',icon: 'mdi-pine-tree',},],},
];
const getChildById = (_id, _id2 = false)=>{
for(const main of arr){
if(_id2 !== false && main.id !== _id2)continue;
for(const child of main.children){
if(child.id === _id)return child;
}
}
return false;
};
(async()=>{
const i_want_this_id = 8;
//
const a = getChildById(i_want_this_id);
console.log('Found', a);
})();
function findChildrenById(data, cid) {
for (let p = 0; p < data.length; p += 1) {
if (data[p].id === cid) return data[p].name
let res = data[p].children.find(c => c.id === cid)
if (res) return res.name
}
return null
}
const arr = [{
id: 0,
name: 'Biomes',
icon: 'mdi-image-filter-hdr',
isParent: true,
children: [{
id: 1,
name: 'Redwood forest',
icon: 'mdi-image-filter-hdr'
}]
}, {
id: 2,
name: 'Trees',
icon: 'mdi-pine-tree',
children: [{
id: 8,
name: 'Redwood',
icon: 'mdi-pine-tree'
}]
}];
console.log(findChildrenById(arr, 2))
One way is by using Array find() method
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
let object= arr.find((item)=>item.id === "your ID");
console.log(object?.name);
const arr = [{
id: 0,
name: 'Biomes',
icon: 'mdi-image-filter-hdr',
isParent: true,
children: [{
id: 1,
name: 'Redwood forest',
icon: 'mdi-image-filter-hdr'
}]
},{
id: 2,
name: 'Trees',
icon: 'mdi-pine-tree',
children: [{
id: 8,
name: 'Redwood',
icon: 'mdi-pine-tree'
}]
}];
let object= arr.find((item)=>item.id === 2);
console.log(object?.name);
let object= arr.find((item)=>item.id === "your ID");
console.log(object?.name);
Related
I have an array and want to change name in object { id: 4, name: 'name4' } to 'name6'
const example = [
{
id: '1234',
desc: 'sample1',
items: [
{ id: 1, name: 'name1' },
{ id: 2, name: 'testItem2' }
]
},
{
id: '3456',
desc: 'sample2',
items: [
{ id: 4, name: 'name4' },
{ id: 5, name: 'testItem5' }
]
},
I try in this way but it isn't working
const name = 'name4';
const result = example?.forEach((group) =>
group.items.forEach((item) =>
if (item.name === name) {
return item.name === 'name6';
}
return null;
})
);
The for...of statement is my recommendation for readability and loop optimisation.
const example = [
{
id: '1234',
desc: 'sample1',
items: [
{ id: 1, name: 'name1' },
{ id: 2, name: 'testItem2' },
],
},
{
id: '3456',
desc: 'sample2',
items: [
{ id: 4, name: 'name4' },
{ id: 5, name: 'testItem5' },
],
},
];
const oldName = 'name4';
const newName = 'name6';
for (const group of example) {
for (const item of group.items) {
if (item.name === oldName) {
item.name === newName;
break
}
}
}
You could even go a step further and terminate the outer loop with a label if you only need to change the name in a single group.
outerLoop: for (const group of example) {
for (const item of group.items) {
if (item.name === oldName) {
item.name === newName;
break outerLoop;
}
}
}
Hope this helps.
You could either change the value by simply assigning a new value.
example[1].items[0].name = 'name6'
But you can also iterate through all items and search for the name you want to change. I created a function that goes through an array and loops over its nested items arrays searching for any given name (targetName) and replacing it with a new one (newName):
function changeName(array, targetName, newName) {
// Loop through the elements of array
array.forEach((element) => {
// Check each item: change the name if it matches the target
element.items.forEach((item) => {
if (item.name === targetName) item.name = newName;
});
});
}
// This function will check example array and change
// every name that has a value 'name4' into 'name6'
changeName(example, "name4", "name6");
forEach doesn't return any value.
Instead of return item.name === 'name6' you can simply set new value to item.name.
Why not like this?
const example = [{
id: '1234',
desc: 'sample1',
items: [{
id: 1,
name: 'name1'
},
{
id: 2,
name: 'testItem2'
}
]
},
{
id: '3456',
desc: 'sample2',
items: [{
id: 4,
name: 'name4'
},
{
id: 5,
name: 'testItem5'
}
]
},
]
example[1].items[0].name = 'name6'
console.log(example)
getComponentById: (state) => (componentId) => {
return state.articles
.filter(article => Object.keys(article).some(key => {
return ['maps', 'charts', 'tables'].includes(key);
}))
.reduce((acc, article) => {
acc = article.components?.find(c => c.id == componentId);
if (acc) return acc;
acc = article.maps?.find(c => c.id == componentId);
if (acc) return acc;
acc = article.charts?.find(c => c.id == componentId);
if (acc) return acc;
acc = article.tables?.find(c => c.id == componentId);
if (acc) return acc;
})
}
Wonder if there's a better way to rewrite this because the list of components might grow so it feels wrong to just keep adding the lines.
If the id is unique can you just look into every key on every article?
If my guess at your data structure is close you should be able to do something like this
let articles = [
{
maps: [{ id: 1, name: 'map1' }, { id: 2, name: 'map2' }],
charts: [{ id: 3, name: 'charts1' }, { id: 4, name: 'charts2' }],
tables: [{ id: 5, name: 'tables1' }, { id: 6, name: 'tables2' }]
},
{
maps: [{ id: 7, name: 'map3' }, { id: 8, name: 'map4' }],
charts: [{ id: 9, name: 'charts3' }, { id: 10, name: 'charts4' }],
tables: [{ id: 11, name: 'tables3' }, { id: 12, name: 'tables4' }]
}
]
let getComponentById = (componentId) => {
let result = null;
articles.forEach(article => {
Object.keys(article).forEach(key => {
let component = article[key].find(x=> x.id == componentId);
if(component) {
result = component;
}
});
});
return result;
}
console.log(getComponentById(3));
console.log(getComponentById(12));
Credit to #IrKenInvader's answer, I copy data from him.
I use for loop because once you find a component, you can early return and no need to check the rest of the data.
let state = {
articles: [
{
maps: [
{ id: 1, name: "map1" },
{ id: 2, name: "map2" },
],
charts: [
{ id: 3, name: "charts1" },
{ id: 4, name: "charts2" },
],
tables: [
{ id: 5, name: "tables1" },
{ id: 6, name: "tables2" },
],
},
{
maps: [
{ id: 7, name: "map3" },
{ id: 8, name: "map4" },
],
charts: [
{ id: 9, name: "charts3" },
{ id: 10, name: "charts4" },
],
tables: [
{ id: 11, name: "tables3" },
{ id: 12, name: "tables4" },
],
},
],
};
const getComponentById = state => componentId => {
for (let i = 0; i < state.articles.length; i++) {
const filteredKey = Object.keys(state.articles[i]).filter(key =>
["maps", "charts", "tables"].includes(key)
);
for (let j = 0; j < filteredKey.length; j++) {
const foundComponent = state.articles[i][filteredKey[j]].find(
a => a.id == componentId
);
if (foundComponent) return foundComponent;
}
}
return null;
};
const output = getComponentById(state)(12);
console.log(output);
<script> var itemsTemp= [
{ id: 0, text: 'Andy' },
{
id: 1, text: 'Harry',
children: [
{ id: 2, text: 'David' }
]
},
{ id: 3, text: 'Lisa' },
{ id: 4, text: 'Mona' },
{ id: 5, text: 'Ron' },
{ id: 6, text: 'Joe' }
];
var items = itemsTemp;
var filtered = items.filter(function(item) {
return item.id !== 3;
});
console.log(filtered);
</script>
in this way, I can only remove the parent but how can I delete the child object? please help me to fix this
Since you want to filter children, you can use .reduce() to perform a mapping and filtering of your array. When you reach an object which has a children property, you can recursively call your function to then perform the mapping/filtering on the child array .reduce() array like so:
const items = [{ id: 0, text: 'Andy' }, { id: 1, text: 'Harry', children: [{ id: 2, text: 'David' }] }, { id: 3, text: 'Lisa' }, { id: 4, text: 'Mona' }, { id: 5, text: 'Ron' }, { id: 6, text: 'Joe' } ];
const filterItems = (items, fn) => items.reduce((acc, item) => {
if(item.children)
return [...acc, ...filterItems(item.children, fn)];
else if(fn(item))
return [...acc, item];
return acc;
}, []);
const filtered = filterItems(items, item => item.id !== 2);
console.log(filtered);
If you don't want to remove the item from the parent list, and only from the child list, then you push an update object instead:
const items = [{ id: 0, text: 'Andy' }, { id: 1, text: 'Harry', children: [{ id: 2, text: 'David' }] }, { id: 3, text: 'Lisa' }, { id: 4, text: 'Mona' }, { id: 5, text: 'Ron' }, { id: 6, text: 'Joe' } ];
const toRemoveId = 2;
const filterItems = (items, fn) => items.reduce((acc, item) => {
if(item.children)
return [...acc, {...item, children: filterItems(item.children, fn)}];
else if(fn(item))
return [...acc, item];
return acc;
}, []);
const filtered = filterItems(items, item => item.id !== 2);
console.log(filtered);
This will work for arbitrary object depths.
I just wrote the filterById function I think it works for your case
var itemsTemp = [
{ id: 0, text: "Andy" },
{
id: 1,
text: "Harry",
children: [{ id: 2, text: "David" }],
},
{ id: 3, text: "Lisa" },
{ id: 4, text: "Mona" },
{ id: 5, text: "Ron" },
{ id: 6, text: "Joe" },
];
var items = itemsTemp;
const filterById = (items, id) => {
return items.reduce((accumulator, currentValue) => {
if(currentValue.children){
const newCurrentValue = filterById(currentValue.children, id)
currentValue = {...currentValue, children: newCurrentValue}
}
if(currentValue.id !== id){
return [...accumulator, currentValue]
}
return accumulator
},[])
}
console.log(filterById(itemsTemp,2));
console.log(itemsTemp)
I think you can do like this.
var itemsTemp= [
{ id: 0, text: 'Andy' },
{
id: 1, text: 'Harry',
children: [
{ id: 2, text: 'David' }
]
},
{ id: 3, text: 'Lisa' },
{ id: 4, text: 'Mona' },
{ id: 5, text: 'Ron' },
{ id: 6, text: 'Joe' }
];
var items = itemsTemp;
var filtered = items.filter(function(item) {
childrens=item.children;
if(childrens)
{
filteredchildren = childrens.filter(children=>children.id!==2);
item.children=filteredchildren;
}
return item.id !== 2;
});
console.log(filtered);
I want to retrieve all child ids of a specific group, which can be deeply nested or not.
Here is a sample json:
[
{
id: 1,
name: 'Desjardins Group 1',
children: [
{ id: 2, name: 'Analysts', children: [] },
{ id: 3, name: 'Administration', children: [] }
]
},
{
id: 4,
name: 'Desjardins Group 2',
children: [
{ id: 5, name: 'Consultants1', children: [] },
{
id: 6,
name: 'Consultant2',
children: [
{
id: 7, name: 'Interns', children: [
{ id: 8, name: 'subInterns1', children: [] },
{ id: 9, name: 'subInterns2', children: [] },
{ id: 10, name: 'subInterns3', children: [] }
]
}
]
}
]
}
]
I'm trying to make a function that takes an id has a parameter, and return all child ids.
Ex: getChildGroups(6) would return 7, 8, 9 and 10.
I guess recursive function and filters are the way to go, but i can't find a proper example.
Here's a simplified version of Johann Bauer's answer.
The first function just finds the first node that matches the given ID, with no need for any accumulation of data:
function findNode(data, id) {
if (!Array.isArray(data)) return;
for (let entry of data) {
if (entry.id === id) {
return entry;
} else {
const node = findNode(entry.children, id);
if (node) {
return node;
}
}
}
}
This second function just gets the child IDs, storing them in the passed array, without any intermediate arrays being created:
function getChildIds(node, result = []) {
if (!node) return;
if (!Array.isArray(node.children)) return;
for (let entry of node.children) {
result.push(entry.id);
getChildIds(entry, result);
}
return result;
}
It might be a good idea to split your problem into two smaller problems:
Find a group of ID x somewhere nested in the graph
Given a node, return all their sub-node IDs recursively
The solution to the first problem could look something like this:
function findGroupId(o, id) {
if (o.id == id) {
// We found it!
return o;
}
if (Array.isArray(o)) {
// If we start with a list of objects, pretend it is the root node
o = {children: o}
}
let results = [];
for (let c of o.children) {
// recursively call this function again
results.push(findGroupId(c, id))
}
// return the first matching node
return results.filter(r => r !== undefined)[0];
}
And for the second problem:
function getAllChildrenIDs(o) {
if (o.children === undefined)
return [];
let ids = [];
for (c of o.children) {
ids.push(c.id);
// recursively call this function again
for (id of getAllChildrenIDs(c))
ids.push(id);
}
return ids;
}
And if we put this together:
let example = [{
id: 1,
name: 'Desjardins Group 1',
children: [{
id: 2,
name: 'Analysts',
children: []
},
{
id: 3,
name: 'Administration',
children: []
}
]
},
{
id: 4,
name: 'Desjardins Group 2',
children: [{
id: 5,
name: 'Consultants1',
children: []
},
{
id: 6,
name: 'Consultant2',
children: [{
id: 7,
name: 'Interns',
children: [{
id: 8,
name: 'subInterns1',
children: []
},
{
id: 9,
name: 'subInterns2',
children: []
},
{
id: 10,
name: 'subInterns3',
children: []
}
]
}]
}
]
}
];
function findGroupId(o, id) {
if (o.id == id) {
return o;
}
if (Array.isArray(o)) {
o = {
children: o
}
}
let results = [];
for (let c of o.children) {
results.push(findGroupId(c, id))
}
return results.filter(r => r !== undefined)[0];
}
function getAllChildrenIDs(o) {
if (o.children === undefined)
return [];
let ids = [];
for (c of o.children) {
ids.push(c.id);
for (id of getAllChildrenIDs(c))
ids.push(id);
}
return ids;
}
console.log(getAllChildrenIDs(findGroupId(example, 6)))
I have a Object which looks like the following obj.
var obj = [
{ id: 1, name: "animals" },
{ id: 2, name: "animals_cat" },
{ id: 3, name: "animals_dog" },
{ id: 4, name: "animals_weazle" },
{ id: 5, name: "animals_weazle_sand shadow weazle" },
{ id: 11, name: "fruits" },
{ id: 32, name: "fruits_banana" },
{ id: 10, name: "threes" },
{ id: 15, name: "cars" }
];
The Object should be converted into the following scheme:
var items = [
{ id: 11, name: "fruits", items: [
{ id: 32, name: "banana" }
]},
{ id: 10, name: "threes" },
{ id: 1, name: "animals", items: [
{ id: 2, name: "cat" },
{ id: 3, name: "dog" },
{ id: 4, name: "weazle", items: [
{ id: 5, name: "sand shadow weazle" }
]}
]},
{ id: 15, name: "cars" }
];
I tried a lot but unfortunately without any success. I did $.each on obj, did a split('_') on it and pushed it to items. But how can I do it for unlimited depth and push it into the right category?
I'm happy for any help.
Maybe this helps.
It works with Array.prototype.forEach for processing obj, Array.prototype.reduce for getting the right branch and Array.prototype.some for the right array element for inserting the new object.
This proposal works for sorted and consistent data.
var obj = [
{ id: 1, name: "animals" },
{ id: 2, name: "animals_cat" },
{ id: 3, name: "animals_dog" },
{ id: 4, name: "animals_weazle" },
{ id: 5, name: "animals_weazle_sand shadow weazle" },
{ id: 11, name: "fruits" },
{ id: 32, name: "fruits_banana" },
{ id: 10, name: "threes" },
{ id: 15, name: "cars" }
],
tree = [];
obj.forEach(function (a) {
var path = a.name.split('_'),
o = {};
o.id = a.id;
path.reduce(function (r, b) {
o.name = b;
r.some(function (c) {
if (c.name === b) {
c.items = c.items || [];
r = c.items;
return true;
}
});
return r;
}, tree).push(o);
});
document.write('<pre>' + JSON.stringify(tree, 0, 4) + '</pre>');
Update: Version for independent order of items.
var obj = [
{ id: 5, name: "animals_weazle_sand shadow weazle" },
{ id: 32, name: "fruits_banana" },
{ id: 1, name: "animals" },
{ id: 2, name: "animals_cat" },
{ id: 3, name: "animals_dog" },
{ id: 4, name: "animals_weazle" },
{ id: 11, name: "fruits" },
{ id: 10, name: "threes" },
{ id: 15, name: "cars" },
{ id: 999, name: "music_pop_disco_euro"}
],
tree = [];
obj.forEach(function (item) {
var path = item.name.split('_'),
o = tree;
path.forEach(function (a, i) {
var oo = { name: a, items: [] },
last = path.length - 1 === i,
found = o.some(function (b) {
if (b.name === a) {
if (last) {
b.id = item.id;
return true;
}
b.items = b.items || [];
o = b.items;
return true;
}
});
if (!found) {
if (last) {
o.push({ id: item.id, name: a });
} else {
o.push(oo);
o = oo.items;
}
}
});
});
document.write('<pre>' + JSON.stringify(tree, 0, 4) + '</pre>');