I have the following scenario: I have an array of objects (TreeItems) which could contain nested arrays. And I have another Array with indexes. Now I would like to change the TreeItems array using the indexes array to access a specific tree item and depending on if there is a nested array called "items" I would like to either append the existing one or add a new one. And mostly important, I would like to get rid of the evil eval.
So in my example I try to add a new entry at TreeItems[4][0][0] but the indexes in the brackets should be added dynamically from the indexes array but I’m not quite shure how to do this.
I have searched of course and found this one, but it doesn’t quite work for me.
Any help would be greatly appreciated.
const TreeItems = [
{ text: "Lvl 0", items: [{ text: "Lvl 0-0" }, { text: "Lvl 0-1" }] },
{ text: "Lvl 1" },
{ text: "Lvl 2", items: [{ text: "Lvl 2-0" }, { text: "Lvl 2-1" }] },
{ text: "Lvl 3" },
{
text: "Lvl 4",
items: [
{
text: "Lvl 4-0",
items: [
{
text: "Lvl 4-0-0",
items: [{ text: "Lvl 4-0-0-0" }, { text: "Lvl 4-0-0-1" }, { text: "Lvl 4-0-0-2" }],
},
{ text: "Lvl 4-0-1" },
{ text: "Lvl 4-0-2" },
],
},
],
},
];
const indexes = [4,0,0];
let comString = '';
for(let i = 0; i < indexes.length; i++) {
if(i === 0) {
comString = "TreeItems[" + indexes[i] + "]";
} else {
comString += ".items[" + indexes[i] + "]";
}
}
if(eval(comString).items !== undefined) {
eval(comString).items.push({"text": "Test-item-appended"});
} else {
eval(comString).items = [{"text": "Test-items-added"}]
}
console.log(comString);
console.log(eval(comString).items);
console.log(TreeItems);
2
Answers
You don’t have to use
eval
or any kind of dynamic code generation to do this. As a matter of fact, there are no programming problems that would warrant the use ofeval
unless you’re explicitly looking to execute a block of foreign JavaScript code.If you want to access the nested children of
TreeItems
using an array of indices as the path, you can create a variable for storing the child at depthn
, starting from 0, and then repeatedly overwrite it with the child at depthn + 1
, until you have accounted for all the indices in the path:Note that using for loops and let statements is a bit verbose and goes against the coding style for modern JavaScript. The proper way to do this is by using Array.prototype.reduce, as suggested in the answer you found. The original question was about arrays of arrays, whereas in this case you’re accessing an array of objects with a property named
items
, and your function must account for that.The following code is functionally equivalent to the above code:
Read more about Array.prototype.reduce at MDN.
Once you have obtained a reference to the parent node, you may add items to it in exactly the same manner as proposed in your question:
One possible implementation was a function which utilizes a
reduce
based approach.Such a function does either access an
items
-item through the additionally provided array of sub-items
indices, or it does create such an item at the targeted tree-depth by also creating any not yet existing tree-structure; hence the example’s function name …createAndOrGetItem
.The reducer (callback) function would iterate the array of indices, where with each iteration step the function’s return value either is the to be processed next-level
items
-array or, as for the final iteration, the targeted item itself.Depending on whether an item does exist or whether an existing item does feature an
items
-array one just does access these references or one does create and refer to them. For one of such tasks the provided example code uses the nullish coalescing assignment operator /??=
.