Problem when comparing JavaScript objects in an array - javascript

I have these 2 arrays of information. I want to resolve this problem: (Given the name of an user and a required permission, return true if
the user have that permission and false otherwise.)
const groups = [
{
id: 1,
name: 'admins',
permissions: ['CREATE', 'DELETE', 'READ', 'WRITE'],
},
{
id: 2,
name: 'readers',
permissions: ['READ'],
},
{
id: 3,
name: 'writers',
permissions: ['WRITE'],
},
{
id: 4,
name: 'managers',
permissions: ['CREATE', 'DELETE'],
}
];
const users = [
{
name: 'john',
groups: [1],
},
{
name: 'mary',
groups: [2, 3],
},
{
name: 'alice',
groups: [2]
},
{
name: 'bob',
groups: [3],
},
{
name: 'eve',
groups: [2, 4],
},
];
the code I've written so far:
function userHasPermission(userName, requiredPermission) {
//looping through first array
for (i = 0; i < users.length; i++) {
if (users[i].name === userName) {
// looping through the second array
for (j = 0; j < groups.length; j++) {
//here I don't know how to resolve this comparison, because as far as I understand it, the script has to check if userName is a valid ID inside groups array?
if (users[i].groups.includes(groups[j].id) && (groups[j].permissions.includes(requiredPermission))) {
return true;
} else {
return false;
}
}
}
}
};
userHasPermission("mary", "WRITE");
I'm new to Vanilla JavaScript and I'm stuck at this point, any help will be appreciated!

function userHasPermission(userName, requiredPermission) {
//looping through first array
for (i = 0; i < users.length; i++) {
if (users[i].name === userName) {
// looping through the second array
for (j = 0; j < groups.length; j++) {
if (users[i].groups.includes(groups[j].id) && (groups[j].permissions.includes(requiredPermission))) {
return true;
} else {
continue; // don’t return false yet.
}
}
}
}
// only when you’ve checked all possibilities
// still find no match, now you can return definitely
return false;
};
Above is imperative for-loop style based on your code. I personally prefer more functional code style. For your reference:
function userHasPermission(userName, requiredPermission) {
const targetUser = users.find(user => user.name === userName);
if (!targetUser) return false;
const userGroups = groups.filter(g => targetUser.groups.includes(g.id));
const permissions = userGroups.flatMap(g => g.permissions);
return permissions.includes(requiredPermission);
}

function userHasPermission(userName, requiredPermission) {
const user = users.find(u => u.name === userName);
let result = false;
user.groups.forEach(id => {
const group = groups.find(g => g.id === id);
result = group.permissions.includes(requiredPermission);
});
return result;
};
userHasPermission("mary", "WRITE");

Related

Failed to console unique names from JSON array

I have a JSON array having hundreds of objects where each JSON object having name and hobbies property.
Below is the JSON structure:
const data = [
{
name:'Paul',
hobbies: ['Football','Reading']
},
{
name:'Riya',
hobbies: ['Singing','Dancing']
},
{
name:'Jack',
hobbies: ['Gaming']
}
]
So here if I will iterate through this data it will give me same name multiple times wherever multiple hobbies are present.So if I am console it result would be
Paul,Football
Paul,Reading
Riya,Singing
Riya,Dancing
Jack,Gaming
I don't want above output I want wherever there is same name is coming in a same object don't console it like below:
Paul,Football
"",Reading
Riya,Singing
"",Dancing
Jack,Gaming
Below is my code:
const data = [
{
name:'Paul',
hobbies: ['Football','Reading']
},
{
name:'Riya',
hobbies: ['Singing','Dancing']
},
{
name:'Jack',
hobbies: ['Gaming']
}
]
const example = (data) => {
for(var i=0;i<data.length;i++){
for(var j=0;j<data[i].hobbies.length;j++){
console.log(data[i].name,data[i].hobbies[j]);
if(i=0){
console.log(data[i].name,data[i].reports[j]);
}
else{
const prev = i-1;
if(data[prev].name == data[i].name) { //Getting TypeError here cannot read property 'name' of undefined
console.log("",data[i].reports[j]);
}
else{
console.log(data[i].name,data[i].reports[j]);
}
}
}
}
}
example(data);
In above code I am trying to compare the previous value of name in data array with the current value of name. If it's same then making name field " " else putting name value and for first element for position 0 I am putting value as it is.
Why am I getting this TypeError?
There are several issues, first is typo, you assigned instead of comparing
if (i=0) {
// ^^^^^
console.log(data[i].name,data[i].reports[j]);
}
The rest is logic, all you have to do is to check the index of j
const example = data => {
for (var i = 0; i < data.length; i++) {
for (var j = 0; j < data[i].hobbies.length; j++) {
if (j == 0) {
console.log(data[i].name, data[i].hobbies[j])
} else {
console.log("", data[i].hobbies[j])
}
}
}
}
Full solution
const data = [
{
name: "Paul",
hobbies: ["Football", "Reading"],
},
{
name: "Riya",
hobbies: ["Singing", "Dancing"],
},
{
name: "Jack",
hobbies: ["Gaming"],
},
]
const example = data => {
for (var i = 0; i < data.length; i++) {
for (var j = 0; j < data[i].hobbies.length; j++) {
if (j == 0) {
console.log(data[i].name, data[i].hobbies[j])
} else {
console.log("", data[i].hobbies[j])
}
}
}
}
example(data)
const data = [
{
name:'Paul',
hobbies: ['Football','Reading']
},
{
name:'Riya',
hobbies: ['Singing','Dancing']
},
{
name:'Jack',
hobbies: ['Gaming']
}
]
data.forEach(d => {
d.hobbies.forEach((hobby, index) => {
const name = index == 0 ? d.name : '""'
console.log(name + ',' + hobby)
})
})
Just print the name if index of hobby is 0

How do you combine two like arrays of objects into a single array object?

I'm sorry if I'm asking a question that has been asked before, I had a hard time wording this one.
Basically, my API is returning an array of objects that are alike
const response.survey = [
{
1: { id: 1, user: user_1, points: 5 },
2: { id: 2, user: user_2, points: 3 }...
}
],
[
{
1: { id: 1, user: user_1, points: 10 },
2: { id: 2, user: user_2, points: 0 }...
}
],...
This can carry on for hundreds of arrays potentially, depending on users submissions through our forms. I'm stuck on how to combine them into something like:
[
{ user: user_1, points: 15 },
{ user: user_2, points: 3 },...
]
Note that the data that returned from the api has a key for the object, like Array[Object[1]], Array[Object[2]], etc.
In my current development environment, I'm returning 2x sets of 25 data points, so I would expect my final array of objects to be indexed at 25, but right now I'm getting 52. I believe there is a shorter way to handle this, but I cant quite figure it out and I keep going in circles.
Here is what I've tried:
let newArr = [];
response.survey.map((ballot, i) => {
for (const key in ballot) {
if (newArr.length == 0) {
let newObj = {
name: pos[key].user,
points: pos[key].points
}
newArr.push(newObj);
} else {
for (let k = 0; k < newArr.length; k++) {
if (newArr[k].name === pos[key].name) {
newArr[k].points += pos[key].points;
} else {
if (k + 1 == newArr.length) {
let newObj = {
name: pos[key].name,
points: pos[key].points
}
newArr.push(newObj);
}
}
}
}
}
}
I think I've been working on this issue for so long that I've started to go into circles and heading down the wrong path.
Any help would be greatly appreciated.
This solved my problem
await fetch('/api/getBallots')
.then((response) => response.json())
.then(async (data) => {
let newArray = []
data.ballots.map((ballot, i) => {
for (const key in ballot) {
if(!ballot[key].name) {
return;
} else {
let newObj = {
name: ballot[key].name,
points: ballot[key].points
}
newArray.push(newObj);
}
}
});
let rankings = [];
for (let i = 0; i < newArray.length; i++) {
if (rankings.length == 0) {
rankings.push({
name: newArray[i].name,
points: newArray[i].points
});
} else {
const index = rankings.findIndex((item) => item.name == newArray[i].name);
if (index == -1) {
rankings.push({
name: newArray[i].name,
points: newArray[i].points
})
} else {
rankings[index].points += newArray[i].points;
}
}
}
})

How could I find a json object by id using nodejs/js

So I want to get the object by the id 1 in this object:
let users = {
'users': {
'user1': {
'id': '1',
'name': 'Brandon',
'DOB': '05/04/2000'
},
'user2': {
'id': '2',
'name': 'Jefferson',
'DOB': '05/19/2004'
}
}
}
and I want it to return the entire 'user1' array and log it, does anyone know how I could do this?
I looked all over stackoverflow, and docs, and couldn't find a way to do this. Could I get some help?
There are a few approaches, both of these should roughly achieve what you're looking for:
let users = {
'users': {
'user1': {
'id': '1',
'name': 'Brandon',
'DOB': '05/04/2000'
},
'user2': {
'id': '2',
'name': 'Jefferson',
'DOB': '05/19/2004'
}
}
}
const findUserById = (id) => {
const key = Object.keys(users.users).find(user => users.users[user].id === '1')
return users.users[key]
}
console.log(findUserById('1'))
let users = {
'users': {
'user1': {
'id': '1',
'name': 'Brandon',
'DOB': '05/04/2000'
},
'user2': {
'id': '2',
'name': 'Jefferson',
'DOB': '05/19/2004'
}
}
}
const findUserById = (id) => {
const [key, user] = Object.entries(users.users).find(([key, user]) => user.id === '1');
return user;
}
console.log(findUserById('1'))
While the answer by skovy is right and this is what you should be doing in an actual production setting, I would advise against applying it immediately in your situation.
Why? Your question shows that you first need to learn some basic principles any JavaScript programmer should have, that is:
How to iterate over contents of an object
The simplest method used to iterate over an object's keys is the for .. in loop. When iterating over an object's keys using the for .. in loop, the code inside the curly brackets will be executed once for every key of the object we are iterating.
let users = {
"user1": {
"id": 1
},
"user2": {
"id": 2
}
}
for (let key in users) {
console.log(key);
}
The above code will print:
user1
user2
Proceeding from that, it should be clear how to find the element we want:
let foundUser = null;
for (let key in users) {
if (users[key].id === 1) {
foundUser = users[key];
break;
}
}
// now found user is our user with id === 1 or null, if there was no such user
When not to do that
If you have a complex object which is a descendant of another object and don't want to iterate over inherited properties, you could instead get an array of current object's keys with Object.keys:
let users = {
"user1": {
"id": 1
},
"user2": {
"id": 2
}
}
const keys = Object.keys(users) // now this is an array containing just keys ['user1', 'user2'];
let foundUser = null;
// now you can iterate over the `keys` array using any method you like, e.g. normal for:
for (let i = 0; i < keys.length; i++) {
if (users[keys[i]].id === 1) {
foundUser = users[keys[i]];
break;
}
}
// or alternatively `for of`:
for (for key of keys) {
if (users[key].id === 1) {
foundUser = users[key];
break;
}
}
Other options
You could use Object.values to get an array containing all values of the object:
let users = {
"user1": {
"id": 1
},
"user2": {
"id": 2
}
}
const values = Object.values(users); // values: [{"id":1},{"id":2}]
You can now find the entry you want on your own:
let foundUser = null
for (let i = 0; i < values.length; i++) {
if (values[i].id === 1) {
foundUser = values[i];
break;
}
}
Or using the Array's find method:
let foundUser = values.find(user => user.id === 1);
// now foundUser contains the user with id === 1
Or, shorter and complete version:
let users = {
"user1": {
"id": 1
},
"user2": {
"id": 2
}
}
const foundUser = Object.values(users).find(user => user.id === 1);
// now foundUser is `{ id: 1 }`
Not a big fan of reinventing the wheel. We use object-scan for most of our data processing now. It's very handy when you can just use a tool for that kind of stuff. Just takes a moment to wrap your head around how to use it. Here is how it could answer your questions:
// const objectScan = require('object-scan');
const find = (id, data) => objectScan(['**.id'], {
abort: true,
rtn: 'parent',
filterFn: ({ value }) => value === id
})(data);
const users = { users: { user1: { id: '1', name: 'Brandon', DOB: '05/04/2000' }, user2: { id: '2', name: 'Jefferson', DOB: '05/19/2004' } } };
console.log(find('1', users));
// => { id: '1', name: 'Brandon', DOB: '05/04/2000' }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan
A simple for loop would do it:
let users = {
'users': {
'user1': {
'id': '1',
'name': 'Brandon',
'DOB': '05/04/2000'
},
'user2': {
'id': '2',
'name': 'Jefferson',
'DOB': '05/19/2004'
}
}
}
let desiredUser = {};
Object.keys(users.users).forEach((oneUser) => {
if(users.users[oneUser].id === "1")
desiredUser = users.users[oneUser];
});
console.log(desiredUser);
You can also use reduce for this... as well as if you wanted to return the "entire" object, you could do:
let USER_LIST = {
'users': {
'user1': {
'id': '1',
'name': 'Brandon',
'DOB': '05/04/2000'
},
'user2': {
'id': '2',
'name': 'Jefferson',
'DOB': '05/19/2004'
}
}
}
function findUserById(id){
return Object.entries(USER_LIST.users).reduce((a, [user, userData]) => {
userData.id == id ? a[user] = userData : '';
return a;
}, {});
}
console.log(findUserById(1));
I agree with the Answers. just a short and simple way is to use .find() method
//--data returned-----//
data = [{"Id":22,"Title":"Developer"},{"Id":45,"Title":"Admin"}]
fs.readFile('db.json','utf8', function(err,data){
var obj = JSON.parse(data);
console.log(obj);
var foundItem = obj.find(o=>o.Id==id);
console.log(foundItem);
});

Mapping an org chart into a hash table

Hey all I'm trying to take an array of objects(employees) and map them to a new object in order to depict the hierarchy of the org. So each manager would have a key and an array attached to the key holding all the names of their reports.
I'm unsure why I am unable to push my employee names to their respective manager's array. This seems to set my object keys to arrays rather than 1,2,3,4.
Anyone pointers would be appreciated.
Repl.it: https://repl.it/JeMh/2
let data = [
{
name: 'ceo',
id: 1,
mgr: null,
},
{
name: 'vp1',
id: 2,
mgr: 1,
},
{
name: 'vp2',
id:3,
mgr: 1,
},
{
name: 'mgr',
id:4,
mgr: 2,
},
];
function displayOrg(data) {
let org = {};
for(let i = 0; i < data.length; i++) {
let current = data[i];
for(let key in current){
if(org[key] !== current.id || current.mgr){
org[current.id] = []
}
}
for(let key in org){
console.log(current.mgr);
console.log(org[key])
if(current.mgr === org[key]){
console.log("THIS HAPPEN");
org[key].push(current.name);
}
}
}
return org;
}
displayOrg(data);
expected resulted: { '1': [vp1,vp2], '2': [mgr], '3': [], '4': [] }
looks pretty straight forward:
function getMngd(data, mngrId){
return data.filter(emp => emp.mgr === mngrId).map(emp => emp.name)
}
data.reduce((p,c)=>{
const {id} = c;
p[id] = getMngd(data, id);
return p;
},{})

How to find a node in a tree with JavaScript

I have and object literal that is essentially a tree that does not have a fixed number of levels. How can I go about searching the tree for a particualy node and then return that node when found in an effcient manner in javascript?
Essentially I have a tree like this and would like to find the node with the title 'randomNode_1'
var data = [
{
title: 'topNode',
children: [
{
title: 'node1',
children: [
{
title: 'randomNode_1'
},
{
title: 'node2',
children: [
{
title: 'randomNode_2',
children:[
{
title: 'node2',
children: [
{
title: 'randomNode_3',
}]
}
]
}]
}]
}
]
}];
Basing this answer off of #Ravindra's answer, but with true recursion.
function searchTree(element, matchingTitle){
if(element.title == matchingTitle){
return element;
}else if (element.children != null){
var i;
var result = null;
for(i=0; result == null && i < element.children.length; i++){
result = searchTree(element.children[i], matchingTitle);
}
return result;
}
return null;
}
Then you could call it:
var element = data[0];
var result = searchTree(element, 'randomNode_1');
Here's an iterative solution:
var stack = [], node, ii;
stack.push(root);
while (stack.length > 0) {
node = stack.pop();
if (node.title == 'randomNode_1') {
// Found it!
return node;
} else if (node.children && node.children.length) {
for (ii = 0; ii < node.children.length; ii += 1) {
stack.push(node.children[ii]);
}
}
}
// Didn't find it. Return null.
return null;
Here's an iterative function using the Stack approach, inspired by FishBasketGordo's answer but taking advantage of some ES2015 syntax to shorten things.
Since this question has already been viewed a lot of times, I've decided to update my answer to also provide a function with arguments that makes it more flexible:
function search (tree, value, key = 'id', reverse = false) {
const stack = [ tree[0] ]
while (stack.length) {
const node = stack[reverse ? 'pop' : 'shift']()
if (node[key] === value) return node
node.children && stack.push(...node.children)
}
return null
}
This way, it's now possible to pass the data tree itself, the desired value to search and also the property key which can have the desired value:
search(data, 'randomNode_2', 'title')
Finally, my original answer used Array.pop which lead to matching the last item in case of multiple matches. In fact, something that could be really confusing. Inspired by Superole comment, I've made it use Array.shift now, so the first in first out behavior is the default.
If you really want the old last in first out behavior, I've provided an additional arg reverse:
search(data, 'randomNode_2', 'title', true)
My answer is inspired from FishBasketGordo's iterativ answer. It's a little bit more complex but also much more flexible and you can have more than just one root node.
/**searchs through all arrays of the tree if the for a value from a property
* #param aTree : the tree array
* #param fCompair : This function will receive each node. It's upon you to define which
condition is necessary for the match. It must return true if the condition is matched. Example:
function(oNode){ if(oNode["Name"] === "AA") return true; }
* #param bGreedy? : us true to do not stop after the first match, default is false
* #return an array with references to the nodes for which fCompair was true; In case no node was found an empty array
* will be returned
*/
var _searchTree = function(aTree, fCompair, bGreedy){
var aInnerTree = []; // will contain the inner children
var oNode; // always the current node
var aReturnNodes = []; // the nodes array which will returned
// 1. loop through all root nodes so we don't touch the tree structure
for(keysTree in aTree) {
aInnerTree.push(aTree[keysTree]);
}
while(aInnerTree.length > 0) {
oNode = aInnerTree.pop();
// check current node
if( fCompair(oNode) ){
aReturnNodes.push(oNode);
if(!bGreedy){
return aReturnNodes;
}
} else { // if (node.children && node.children.length) {
// find other objects, 1. check all properties of the node if they are arrays
for(keysNode in oNode){
// true if the property is an array
if(oNode[keysNode] instanceof Array){
// 2. push all array object to aInnerTree to search in those later
for (var i = 0; i < oNode[keysNode].length; i++) {
aInnerTree.push(oNode[keysNode][i]);
}
}
}
}
}
return aReturnNodes; // someone was greedy
}
Finally you can use the function like this:
var foundNodes = _searchTree(data, function(oNode){ if(oNode["title"] === "randomNode_3") return true; }, false);
console.log("Node with title found: ");
console.log(foundNodes[0]);
And if you want to find all nodes with this title you can simply switch the bGreedy parameter:
var foundNodes = _searchTree(data, function(oNode){ if(oNode["title"] === "randomNode_3") return true; }, true);
console.log("NodeS with title found: ");
console.log(foundNodes);
FIND A NODE IN A TREE :
let say we have a tree like
let tree = [{
id: 1,
name: 'parent',
children: [
{
id: 2,
name: 'child_1'
},
{
id: 3,
name: 'child_2',
children: [
{
id: '4',
name: 'child_2_1',
children: []
},
{
id: '5',
name: 'child_2_2',
children: []
}
]
}
]
}];
function findNodeById(tree, id) {
let result = null
if (tree.id === id) {
return tree;
}
if (Array.isArray(tree.children) && tree.children.length > 0) {
tree.children.some((node) => {
result = findNodeById(node, id);
return result;
});
}
return result;}
You have to use recursion.
var currChild = data[0];
function searchTree(currChild, searchString){
if(currChild.title == searchString){
return currChild;
}else if (currChild.children != null){
for(i=0; i < currChild.children.length; i ++){
if (currChild.children[i].title ==searchString){
return currChild.children[i];
}else{
searchTree(currChild.children[i], searchString);
}
}
return null;
}
return null;
}
ES6+:
const deepSearch = (data, value, key = 'title', sub = 'children', tempObj = {}) => {
if (value && data) {
data.find((node) => {
if (node[key] == value) {
tempObj.found = node;
return node;
}
return deepSearch(node[sub], value, key, sub, tempObj);
});
if (tempObj.found) {
return tempObj.found;
}
}
return false;
};
const result = deepSearch(data, 'randomNode_1', 'title', 'children');
This function is universal and does search recursively.
It does not matter, if input tree is object(single root), or array of objects (many root objects). You can configure prop name that holds children array in tree objects.
// Searches items tree for object with specified prop with value
//
// #param {object} tree nodes tree with children items in nodesProp[] table, with one (object) or many (array of objects) roots
// #param {string} propNodes name of prop that holds child nodes array
// #param {string} prop name of searched node's prop
// #param {mixed} value value of searched node's prop
// #returns {object/null} returns first object that match supplied arguments (prop: value) or null if no matching object was found
function searchTree(tree, nodesProp, prop, value) {
var i, f = null; // iterator, found node
if (Array.isArray(tree)) { // if entry object is array objects, check each object
for (i = 0; i < tree.length; i++) {
f = searchTree(tree[i], nodesProp, prop, value);
if (f) { // if found matching object, return it.
return f;
}
}
} else if (typeof tree === 'object') { // standard tree node (one root)
if (tree[prop] !== undefined && tree[prop] === value) {
return tree; // found matching node
}
}
if (tree[nodesProp] !== undefined && tree[nodesProp].length > 0) { // if this is not maching node, search nodes, children (if prop exist and it is not empty)
return searchTree(tree[nodesProp], nodesProp, prop, value);
} else {
return null; // node does not match and it neither have children
}
}
I tested it localy and it works ok, but it somehow won't run on jsfiddle or jsbin...(recurency issues on those sites ??)
run code :
var data = [{
title: 'topNode',
children: [{
title: 'node1',
children: [{
title: 'randomNode_1'
}, {
title: 'node2',
children: [{
title: 'randomNode_2',
children: [{
title: 'node2',
children: [{
title: 'randomNode_3',
}]
}]
}]
}]
}]
}];
var r = searchTree(data, 'children', 'title', 'randomNode_1');
//var r = searchTree(data, 'children', 'title', 'node2'); // check it too
console.log(r);
It works in http://www.pythontutor.com/live.html#mode=edit (paste the code)
no BS version:
const find = (root, title) =>
root.title === title ?
root :
root.children?.reduce((result, n) => result || find(n, title), undefined)
This is basic recursion problem.
window.parser = function(searchParam, data) {
if(data.title != searchParam) {
returnData = window.parser(searchParam, children)
} else {
returnData = data;
}
return returnData;
}
here is a more complex option - it finds the first item in a tree-like node with providing (node, nodeChildrenKey, key/value pairs & optional additional key/value pairs)
const findInTree = (node, childrenKey, key, value, additionalKey?, additionalValue?) => {
let found = null;
if (additionalKey && additionalValue) {
found = node[childrenKey].find(x => x[key] === value && x[additionalKey] === additionalValue);
} else {
found = node[childrenKey].find(x => x[key] === value);
}
if (typeof(found) === 'undefined') {
for (const item of node[childrenKey]) {
if (typeof(found) === 'undefined' && item[childrenKey] && item[childrenKey].length > 0) {
found = findInTree(item, childrenKey, key, value, additionalKey, additionalValue);
}
}
}
return found;
};
export { findInTree };
Hope it helps someone.
A flexible recursive solution that will work for any tree
// predicate: (item) => boolean
// getChildren: (item) => treeNode[]
searchTree(predicate, getChildren, treeNode) {
function search(treeNode) {
if (!treeNode) {
return undefined;
}
for (let treeItem of treeNode) {
if (predicate(treeItem)) {
return treeItem;
}
const foundItem = search(getChildren(treeItem));
if (foundItem) {
return foundItem;
}
}
}
return search(treeNode);
}
find all parents of the element in the tree
let objects = [{
id: 'A',
name: 'ObjA',
children: [
{
id: 'A1',
name: 'ObjA1'
},
{
id: 'A2',
name: 'objA2',
children: [
{
id: 'A2-1',
name: 'objA2-1'
},
{
id: 'A2-2',
name: 'objA2-2'
}
]
}
]
},
{
id: 'B',
name: 'ObjB',
children: [
{
id: 'B1',
name: 'ObjB1'
}
]
}
];
let docs = [
{
object: {
id: 'A',
name: 'docA'
},
typedoc: {
id: 'TD1',
name: 'Typde Doc1'
}
},
{
object: {
id: 'A',
name: 'docA'
},
typedoc: {
id: 'TD2',
name: 'Typde Doc2'
}
},
{
object: {
id: 'A1',
name: 'docA1'
},
typedoc: {
id: 'TDx1',
name: 'Typde Doc x1'
}
},
{
object: {
id: 'A1',
name: 'docA1'
},
typedoc: {
id: 'TDx2',
name: 'Typde Doc x1'
}
},
{
object: {
id: 'A2',
name: 'docA2'
},
typedoc: {
id: 'TDx2',
name: 'Type de Doc x2'
}
},
{
object: {
id: 'A2-1',
name: 'docA2-1'
},
typedoc: {
id: 'TDx2-1',
name: 'Type de Docx2-1'
},
},
{
object: {
id: 'A2-2',
name: 'docA2-2'
},
typedoc: {
id: 'TDx2-2',
name: 'Type de Docx2-2'
},
},
{
object: {
id: 'B',
name: 'docB'
},
typedoc: {
id: 'TD1',
name: 'Typde Doc1'
}
},
{
object: {
id: 'B1',
name: 'docB1'
},
typedoc: {
id: 'TDx1',
name: 'Typde Doc x1'
}
}
];
function buildAllParents(doc, objects) {
for (let o = 0; o < objects.length; o++) {
let allParents = [];
let getAllParents = (o, eleFinded) => {
if (o.id === doc.object.id) {
doc.allParents = allParents;
eleFinded = true;
return { doc, eleFinded };
}
if (o.children) {
allParents.push(o.id);
for (let c = 0; c < o.children.length; c++) {
let { eleFinded, doc } = getAllParents(o.children[c], eleFinded);
if (eleFinded) {
return { eleFinded, doc };
} else {
continue;
}
}
}
return { eleFinded };
};
if (objects[o].id === doc.object.id) {
doc.allParents = [objects[o].id];
return doc;
} else if (objects[o].children) {
allParents.push(objects[o].id);
for (let c = 0; c < objects[o].children.length; c++) {
let eleFinded = null;`enter code here`
let res = getAllParents(objects[o].children[c], eleFinded);
if (res.eleFinded) {
return res.doc;
} else {
continue;
}
}
}
}
}
docs = docs.map(d => buildAllParents(d, objects`enter code here`))
This is an iterative breadth first search. It returns the first node that contains a child of a given name (nodeName) and a given value (nodeValue).
getParentNode(nodeName, nodeValue, rootNode) {
const queue= [ rootNode ]
while (queue.length) {
const node = queue.shift()
if (node[nodeName] === nodeValue) {
return node
} else if (node instanceof Object) {
const children = Object.values(node)
if (children.length) {
queue.push(...children)
}
}
}
return null
}
It would be used like this to solve the original question:
getParentNode('title', 'randomNode_1', data[0])
Enhancement of the code based on "Erick Petrucelli"
Remove the 'reverse' option
Add multi-root support
Add an option to control the visibility of 'children'
Typescript ready
Unit test ready
function searchTree(
tree: Record<string, any>[],
value: unknown,
key = 'value',
withChildren = false,
) {
let result = null;
if (!Array.isArray(tree)) return result;
for (let index = 0; index < tree.length; index += 1) {
const stack = [tree[index]];
while (stack.length) {
const node = stack.shift()!;
if (node[key] === value) {
result = node;
break;
}
if (node.children) {
stack.push(...node.children);
}
}
if (result) break;
}
if (withChildren !== true) {
delete result?.children;
}
return result;
}
And the tests can be found at: https://gist.github.com/aspirantzhang/a369aba7f84f26d57818ddef7d108682
Wrote another one based on my needs
condition is injected.
path of found branch is available
current path could be used in condition statement
could be used to map the tree items to another object
// if predicate returns true, the search is stopped
function traverse2(tree, predicate, path = "") {
if (predicate(tree, path)) return true;
for (const branch of tree.children ?? [])
if (traverse(branch, predicate, `${path ? path + "/" : ""}${branch.name}`))
return true;
}
example
let tree = {
name: "schools",
children: [
{
name: "farzanegan",
children: [
{
name: "classes",
children: [
{ name: "level1", children: [{ name: "A" }, { name: "B" }] },
{ name: "level2", children: [{ name: "C" }, { name: "D" }] },
],
},
],
},
{ name: "dastgheib", children: [{ name: "E" }, { name: "F" }] },
],
};
traverse(tree, (branch, path) => {
console.log("searching ", path);
if (branch.name === "C") {
console.log("found ", branch);
return true;
}
});
output
searching
searching farzanegan
searching farzanegan/classes
searching farzanegan/classes/level1
searching farzanegan/classes/level1/A
searching farzanegan/classes/level1/B
searching farzanegan/classes/level2
searching farzanegan/classes/level2/C
found { name: 'C' }
In 2022 use TypeScript and ES5
Just use basic recreation and built-in array method to loop over the array. Don't use Array.find() because this it will return the wrong node. Use Array.some() instead which allow you to break the loop.
interface iTree {
id: string;
children?: iTree[];
}
function findTreeNode(tree: iTree, id: string) {
let result: iTree | null = null;
if (tree.id === id) {
result = tree;
} else if (tree.children) {
tree.children.some((node) => {
result = findTreeNode(node, id);
return result; // break loop
});
}
return result;
}
const flattenTree = (data: any) => {
return _.reduce(
data,
(acc: any, item: any) => {
acc.push(item);
if (item.children) {
acc = acc.concat(flattenTree(item.children));
delete item.children;
}
return acc;
},
[]
);
};
An Approach to convert the nested tree into an object with depth 0.
We can convert the object in an object like this and can perform search more easily.
The following is working at my end:
function searchTree(data, value) {
if(data.title == value) {
return data;
}
if(data.children && data.children.length > 0) {
for(var i=0; i < data.children.length; i++) {
var node = traverseChildren(data.children[i], value);
if(node != null) {
return node;
}
}
}
return null;
}

Categories

Resources