I'm really not sure how to word this issue but I will try my best.
I have a nested array:
const items = [
{
id: 1,
name: "Test name",
children: [
{
id: 5,
name: "Test name 5",
children: [
{
id: 6,
name: "Test name 6",
children: [],
},
],
},
],
},
{
id: 8,
name: "Test name 8",
children: [
{
id: 9,
name: "Test name 9",
children: [],
},
],
},
];
and I have an array of indexes where to target and update
const keys = [0,0,0]
The array of indexes should target Test name 6
How can I update Test name 6 to something else?
p.s. items and keys are dynamic. there might be dozens of nested items or dozens of indexes in keys
3 Answers 3
You could reduce the indices and check the children property.
After getting the final object, just assign the value to the wanted property.
const
getItem = (children, keys) => keys.reduce(
({ children = [] }, i) => children[i],
{ children }
),
items = [{ id: 1, name: "Test name", children: [{ id: 5, name: "Test name 5", children: [{ id: 6, name: "Test name 6", children: [] }] }] }, { id: 8, name: "Test name 8", children: [{ id: 9, name: "Test name 9", children: [] }] }],
keys = [0, 0, 0];
console.log(getItem(items, keys));
Comments
To update your nested array first you will first need to change it to a mutable variable, i.e. from a const to a let or var.
From there you can update the value with something like:
// Copy pasted data from your post
let items = [
{
id: 1,
name: "Test name",
children: [
{
id: 5,
name: "Test name 5",
children: [
{
id: 6,
name: "Test name 6",
children: [],
},
],
},
],
},
{
id: 8,
name: "Test name 8",
children: [
{
id: 9,
name: "Test name 9",
children: [],
},
],
},
];
const keys = [0,0,0]
// <----------------- CODE BELOW ---------------->
// var to store item each time you dive deeper into the nested array
// initalize with the first item
let item = items[keys[0]].children;
// loop through each key
for(let i = 1; i < keys.length; i++){
// if we are at the last key, set item equal to the item object
if(i == keys.length-1){
item = item[keys[i]];
}
// otherwise, set item equal to the item's children
else{
item = item[keys[i]].children
}
}
// once item has been reached - all keys have been looped through - data can be minupulated
item.name = "new value"
// "Test name 6" has been updated to "new value"
emphasized text
Comments
In this scenario, I usually use a Map referencing the items within the nested arrays. For example:
const items = [
{
id: 1,
name: "Test name",
children: [
{
id: 5,
name: "Test name 5",
children: [
{
id: 6,
name: "Test name 6",
children: [],
},
],
},
],
},
{
id: 8,
name: "Test name 8",
children: [
{
id: 9,
name: "Test name 9",
children: [],
},
],
},
];
const createItemsMap = items => {
const m = new Map();
(function _collect(items) {
for (const item of items) {
m.set(item.id, item);
if (item.children) {
_collect(item.children);
}
}
})(items);
return m;
}
const itemsMap = createItemsMap(items);
// modify item #6
itemsMap.get(6).name = "Modified name 6";
console.log("itemsMap.get(1) === items[0] is", itemsMap.get(1) === items[0]);
console.log(items);
With that map, modifying the items is simply a matter of doing this :
const keys = [1,5,6];
for (const key of keys)
const item = itemsMap.get(key);
// update item
item.name = `Updated name ${key}`;
item.newProp = 'New value!';
}
// ex:
console.log(items[0]);
// {
// id: 1,
// name: "Updated name 1",
// newProp: "New value!',
// children: [
// {
// id: 5,
// name: "Updated name 5",
// newProps: "New value!",
// children: [
// {
// id: 6,
// name: "Updated name 6",
// newProps: "New value!",
// children: [],
// },
// ],
// },
// ],
// },
And a few more freebies :
// get all existing keys
const keys = Array.from(itemsMap.keys());
// check if an item exists
const exists3 = itemsMap.has(3); // false
const exists5 = itemsMap.has(5); // true
// adding new children
const newChild = { id: 3, name: "Test name 3", children: [] };
// ... add as child of 5
itemsMap.get(5).children.push(newChild);
// ... add it to the map
itemsMap.set(newChild.id, newChild); // that's it!
// remove an item
const removed = itemsMap.get(3);
// ... find if there is a parent...
const parent = Array.from(itemsMap.values()).find(item =>
item.children.includes(removed)
);
if (parent) {
// ... if there is a parent, remove the child
parent.children = parent.children.filter(item =>
item !== removed
);
} else {
// ... otherwise it is a root item, so remove it
items = items.filter(item => item !== removed);
}
// ... remove from map
itemsMap.delete(removed.id);
Note: If items is modified directly (i.e. items are added or removed), then itemsMap needs to either be re-generated, or preferably updated. In any other case, both items and itemsMap reference to the same data.