Reputation: 7982
I've got this data:
const items = [
{
_id: 0,
content: 'Item 1 something',
note: 'Some note for item 1'
},
{
_id: 5,
content: 'Item 1.1 something',
note: 'Some note for item 1.1'
},
{
_id: 1,
content: 'Item 2 something',
note: 'Some note for item 2',
subItems: [
{
_id: 2,
parent_id: 1,
content: 'Sub Item 1 something',
subItems: [{
_id: 3,
parent_id: 2,
content: 'Sub Sub Item 4'
}]
}
]
}
];
Using Javascript, how can I navigate/insert into the tree, provided at any point I have the _id of one item in the tree.
For example, some case scenarios:
How do I navigate the tree using only an _id?
Upvotes: 3
Views: 1223
Reputation: 1860
it's not as easy as "navigating".. the following function will loop through the object and provide things that you can use to achieve what you want.. like name, values, types, number of children and depths.
also look below for specific examples for your case
you simply call it like this: loopThrough(items)
and watch your console for details.
function loopThrough(obj, depth) {
if (typeof(depth) === "undefined") {
depth = 0; // depth 0 means the "root" of your object
} else {
depth++ // increase depth if exist... depth 1 means a property of an object on depth 0
}
for (keyName in obj) {
let thisObj = obj[keyName] //value of this object
let type = thisObj.constructor.name // type: Array, Object, String, Number or Function...
if (type === "Object" || type === "Array") { // to check if this object "have children" to loop through
let childCount = type === "Object" ? Object.keys(thisObj).length : thisObj.length
console.group(depth + " (" + type + ") " + keyName + " : " + childCount) // starts a collapsable group called: depth, type and key
loopThrough(thisObj, depth) //loop through the child object
console.groupEnd() // closes the group
} else { // doesn't have children (a String, Number or Function)
console.log(depth + " (" + type + ") " + keyName + " : " + thisObj) // types: depth, type key and value
}
}
}
here's an example targeting _id:3
in this example I added a sibling to the wanted key.
loopThrough(items, "_id", 3)
function loopThrough(obj, wantedKey = "", wantedValue = "", depth) {
if (typeof(depth) === "undefined") {
depth = 0;
} else {
depth++
}
for (keyName in obj) {
let thisObj = obj[keyName]
let type = thisObj.constructor.name
if (type === "Object" || type === "Array") {
let childCount = type === "Object" ? Object.keys(thisObj).length : thisObj.length
loopThrough(thisObj, wantedKey, wantedValue, depth)
}
if (keyName === wantedKey && thisObj === wantedValue){
siblings = Object.keys(obj)
console.log('%c Hello!, I am ' + wantedKey +":"+ wantedValue, 'color: green');
console.log('%c I have '+ siblings.length + " siblings: " + siblings.toString(), 'color: green');
console.log("%c adding a new sibling...", 'color: grey')
obj["new_sibling"] = "new_sibling_value" // add a sibling to _id 3
siblings = Object.keys(obj)
console.log('%c now I have '+ siblings.length + " siblings: " + siblings.toString(), 'color: green');
console.log('%c I am at depth ' + depth, 'color: blue');
console.log('%c it should be simple to find a way to get my parent _id at depth ' + (depth - 1) , 'color: blue');ParentID
console.log(JSON.stringify(items, null, 4));
}
}
}
for your 2nd request you'll have to tweak the function to store the depth of the wanted key and look for its parent _id
at depth - 1
by recalling the function or creating another one
for the third request you can count++
the keys and once you find the wantedKey
you store the count and loop through again and look for the count - 1
aka previous sibling or count + 1
aka next sibling
as you can see, it's not a simple task, but it's totally possible with some creativity, best of luck.
Upvotes: 0
Reputation: 386730
You could iterate the array and test if _id
property has the wanted value. Then save either the node, the parent or the next item of the array.
For getting the parent node, the actual parent is saved as a closure and returned if the wanted _id
is found.
All functions test for subItems
as array and if, it performs an iteration over subItems
.
function getNode(array, id) {
var node;
array.some(function iter(a) {
if (a._id === id) {
node = a;
return true;
}
return Array.isArray(a.subItems) && a.subItems.some(iter);
});
return node;
}
function getParent(array, id) {
var parent ;
array.some(function iter(p) {
return function (a) {
if (a._id === id) {
parent = p;
return true;
}
return Array.isArray(a.subItems) && a.subItems.some(iter(a));
};
}(undefined));
return parent;
}
function getNextNode(array, id) {
var node;
array.some(function iter(a, i, aa) {
if (a._id === id) {
node = aa[i + 1];
return true;
}
return Array.isArray(a.subItems) && a.subItems.some(iter);
});
return node;
}
var items = [{ _id: 0, content: 'Item 1 something', note: 'Some note for item 1' }, { _id: 5, content: 'Item 1.1 something', note: 'Some note for item 1.1' }, { _id: 1, content: 'Item 2 something', note: 'Some note for item 2', subItems: [{ _id: 2, parent_id: 1, content: 'Sub Item 1 something', subItems: [{ _id: 3, parent_id: 2, content: 'Sub Sub Item 4' }] }] }];
console.log(getNode(items, 3));
console.log(getParent(items, 2));
console.log(getNextNode(items, 5));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 2
Reputation: 4774
I've developed solution for issues like that. I called it backlooker. My function looks:
var backlooker = function(obj) {
for (key in obj) {
if (obj[key]._) {
break;
}
if (obj[key] instanceof Object) {
obj[key]._ = obj;
backlooker(obj[key])
}
}
return obj;
}
You must improve your object first:
items = backlooker(items);
And now you are able to do sth like this:
a = items[2].subItems[0].subItems[0];
c = a._._._._._._._;
c == items; //true
Only one issue: code will not work properly if you already have keys named _
in your object (I think that's very rare situation, but possible).
Upvotes: 0
Reputation: 944076
Using Javascript, how can I navigate/insert into the tree, provided at any point I have the _id of one item in the tree.
You have to recursively loop over the tree and keep track of where you are and where you have been.
JavaScript references point in one direction. Given nothing but a reference to the object with _id 1, you have no connect to the array it is in at all. (It could even exist in multiple arrays or multiple places in the same array).
In general, you need to search the tree (recursion is your friend) and track the indexes of the members you care about.
Once you know the indexes you are dealing with, you can use splice.
Upvotes: 0