Navigate programatically through nested object in JavaScript - javascript

I have a nested object and a path, which describes a position in the object. I would like to access the object at the end of the tree with the given path. For example I would like to get the id of the object, where the path is "contracts/access/roles/MinterRole.sol". How could I do it? Is it possible without recursively iterating over the whole tree? Thank you!
My object looks like the following:
{
"name":"contracts",
"toggled":true,
"id":0,
"children":[
{
"name":"access",
"toggled":false,
"id":1,
"children":[
{
"name":"Roles.sol",
"id":2,
"path":"contracts/access/Roles.sol",
"dependencies":[
]
},
{
"name":"roles",
"toggled":false,
"id":3,
"children":[
{
"name":"CapperRole.sol",
"id":4,
"path":"contracts/access/roles/CapperRole.sol",
"dependencies":[
{
"fileName":"Roles.sol",
"absolutePath":"contracts/access/Roles.sol"
}
]
},
{
"name":"MinterRole.sol",
"id":5,
"path":"contracts/access/roles/MinterRole.sol",
"dependencies":[
{
"fileName":"Roles.sol",
"absolutePath":"contracts/access/Roles.sol"
}
]
},
{
"name":"PauserRole.sol",
"id":6,
"path":"contracts/access/roles/PauserRole.sol",
"dependencies":[
{
"fileName":"Roles.sol",
"absolutePath":"contracts/access/Roles.sol"
}
]
},
{
"name":"SignerRole.sol",
"id":7,
"path":"contracts/access/roles/SignerRole.sol",
"dependencies":[
{
"fileName":"Roles.sol",
"absolutePath":"contracts/access/Roles.sol"
}
]
}
]
}
]
}
]
}

I'd split the path at the slash, then just iterate through it. It makes it easier if you wrap the entire object in an array as well.
const pathParts = path.split("/");
let currentNode = mainTree;
pathParts.forEach(part => {
if (currentNode && currentNode.children) {
currentNode = currentNode.children.find(child => child.name === part);
}
});
Note that while this is iterative, it will still stop at the node on a given level that matches the path name, skipping anything after. As long as the tree isn't ordered (i.e. a binary tree or something), that's the best you can do.

Recursion is the way to go.
You may want to take a look at the semi-famous walk the DOM snippet by D. Crockford himself.
What you want to do is to adapt that snippet to your data and augment the code with extra logic to stop the recursion if a result has been found.

Related

Lodash group array by key

I have the following JSON Structure:
{
"list":[
{
"id":"7ccba2c3-e6df-4cb2-94b9-2f6320366ce0",
"name":"List 1",
"people":[
{"id":"b6cb6317-8f89-4826-b60b-aa0ecf51b1af","name":"Person 1","mediaAccounts":[{"socialNetwork":"facebook"},{"socialNetwork":"twitter"}]},
{"id":"f2620c63-37cc-45be-86e0-fff5d415e483","name":"Person 2","mediaAccounts":[{"socialNetwork":"twitter"},{"socialNetwork":"facebook"}]},
{"id":"e8ccae28-6695-435b-ba61-dbf569e75665","name":"Person 3","mediaAccounts":[{"socialNetwork":"facebook"},{"socialNetwork":"twitter"}]}
]
},
{
"id":"a2d1d55a-e6df-4cb2-94b9-000000011212",
"name":"List 2",
"people":[
{"id":"b6cb6317-8f89-4826-b60b-aa0ecf51b1af","name":"Person 4","mediaAccounts":[{"socialNetwork":"facebook"},{"socialNetwork":"twitter"}]},
{"id":"f2620c63-37cc-45be-86e0-fff5d415e483","name":"Person 5","mediaAccounts":[{"socialNetwork":"twitter"},{"socialNetwork":"facebook"}]},
{"id":"e8ccae28-6695-435b-ba61-dbf569e75665","name":"Person 6","mediaAccounts":[{"socialNetwork":"facebook"},{"socialNetwork":"twitter"}]}
]
}
]
}
and I need to group people in a only one array like this:
{
"people": [
{"id":"b6cb6317-8f89-4826-b60b-aa0ecf51b1af","name":"Person 1","mediaAccounts":[{"socialNetwork":"facebook"},{"socialNetwork":"twitter"}]},
{"id":"f2620c63-37cc-45be-86e0-fff5d415e483","name":"Person 2","mediaAccounts":[{"socialNetwork":"twitter"},{"socialNetwork":"facebook"}]},
{"id":"e8ccae28-6695-435b-ba61-dbf569e75665","name":"Person 3","mediaAccounts":[{"socialNetwork":"facebook"},{"socialNetwork":"twitter"}]},
{"id":"b6cb6317-8f89-4826-b60b-aa0ecf51b1af","name":"Person 4","mediaAccounts":[{"socialNetwork":"facebook"},{"socialNetwork":"twitter"}]},
{"id":"f2620c63-37cc-45be-86e0-fff5d415e483","name":"Person 5","mediaAccounts":[{"socialNetwork":"twitter"},{"socialNetwork":"facebook"}]},
{"id":"e8ccae28-6695-435b-ba61-dbf569e75665","name":"Person 6","mediaAccounts":[{"socialNetwork":"facebook"},{"socialNetwork":"twitter"}]}
]
}
using underscore groupBy function, the return remains the same.
Anyone has any idea how can I group this content above?
Thanks in advance.
You can use Array.flatMap() (or lodash's _.flatMap()) to flatten the list of people to as single array:
const data = {"list":[{"id":"7ccba2c3-e6df-4cb2-94b9-2f6320366ce0","name":"List 1","people":[{"id":"b6cb6317-8f89-4826-b60b-aa0ecf51b1af","name":"Person 1","mediaAccounts":[{"socialNetwork":"facebook"},{"socialNetwork":"twitter"}]},{"id":"f2620c63-37cc-45be-86e0-fff5d415e483","name":"Person 2","mediaAccounts":[{"socialNetwork":"twitter"},{"socialNetwork":"facebook"}]},{"id":"e8ccae28-6695-435b-ba61-dbf569e75665","name":"Person 3","mediaAccounts":[{"socialNetwork":"facebook"},{"socialNetwork":"twitter"}]}]},{"id":"a2d1d55a-e6df-4cb2-94b9-000000011212","name":"List 2","people":[{"id":"b6cb6317-8f89-4826-b60b-aa0ecf51b1af","name":"Person 4","mediaAccounts":[{"socialNetwork":"facebook"},{"socialNetwork":"twitter"}]},{"id":"f2620c63-37cc-45be-86e0-fff5d415e483","name":"Person 5","mediaAccounts":[{"socialNetwork":"twitter"},{"socialNetwork":"facebook"}]},{"id":"e8ccae28-6695-435b-ba61-dbf569e75665","name":"Person 6","mediaAccounts":[{"socialNetwork":"facebook"},{"socialNetwork":"twitter"}]}]}]}
const result = {
people: data.list.flatMap(o => o.people)
}
console.log(result)

How to filter an array based on words in another array?

I want to filter some words contained in an array, contained in a string. For example:
Inputs
1. "disappear" definitions:
[ "Cease to be visible.",
"Cease to exist or be in use.",
"Be lost or go missing, become IMPOSSIBLE to find.",
"Abduct or arrest and kill or detain (a person) for political reasons,
without making their fate known." ]
2. banned words:
[ 'without', 'impossible' ]
Result
[ "Cease to be visible.", "Cease to exist or be in use." ]
I think, I'm very close to the answer:
function filterDefinition (defs, badWords) {
const definitionFilter = defs.filter(function(def) {
if (def.includes(badWords || badWords.toUpperCase()) {
return !defs
}
});
return definitionFilter;
}
Like this?
function filterDefinition (defs, badWords) {
return defs.filter(function(def) {
return !badWords.some(badWord => def.includes(badWord) || def.includes(badWord.toUpperCase()));
});
}
String.includes(string) does not work on arrays.

Converting the one form of JSON Array Data to another?

I'm new to software Field. i have a json array of objects like
var treeObj = [
{
"name": "sriram",
"refernce_id": "SAN001",
"sponer_id": "SAN000"
},
{
"name": "neeraja",
"refernce_id": "SAN002",
"sponer_id": "SAN001"
},
{
"name": "upender",
"refernce_id": "SAN003",
"sponer_id": "SAN001"
},
{
"name": "manoj",
"refernce_id": "SAN004",
"sponer_id": "SAN002"
},
{
"name": "shirisha",
"refernce_id": "SAN005",
"sponer_id": "SAN002"
},
{
"name": "ragu",
"refernce_id": "SAN006",
"sponer_id": "SAN003"
},
{
"name": "santhu",
"refernce_id": "SAN007",
"sponer_id": "SAN003"
}
];
Here i will pass the above object to a function. in that function i need to compare reference id with sponer_id in every object and if they are equal we need to push them into an Array which we call as child object just like below and again we need to check in the child Array that the reference id in is present in sponer_id of above object if it is present again we need to push them to child array into object which contains the reference_id. the final array Object looks like.
[
{
"name": "sriram",
"parent": null,
"children": [
{
"name": "neeraja",
"parent": "sriram",
"children": [
{
"name": "manoj",
"parent": "neeraja"
},
{
"name": "shirisha",
"parent": "neeraja"
}
]
},
{
"name": "upender",
"parent": "sriram",
"children": [
{
"name": "ragu",
"parent": "neeraja"
},
{
"name": "santhu",
"parent": "neeraja"
}
]
}
]
}
]
Here sriram's reference id of treeObj is present as sponer id in neeraja and upender object. so neeraja and upender becomes child to sriram. and neeraja's reference_id is present as sponer_id in manoj and shirisha objects of treeObj. simultaneously the child can many more child objects and we need to format the object dynamically.
The function which i wrote looks like
var mainArr = [], subArrs = [], subObj={}, subIds = [], find = "SAN001";
formatData(treeObj);
function formatData(treeObj){debugger;
var arr = [];
for(var x=0; x<= treeObj.length-1; x++){debugger;
var sampData = treeObj[x];
if(find == sampData.sponer_id){
arr.push(sampData.refernce_id);
subArrs.push(sampData);
}
}
subIds.push(arr);
console.log(subIds);
console.log(subArrs);
formatData(subArrs);
}
please guide me where i went wrong. thanks in advance.
//1. find all items the have no parent and push them on a stack like so:
let stack = treeObj.reduce((list, item) => {
if (<ids match>) list.push(item);
return item;
}, []),
let result = [].concat(stack);
//2. while loop the stack:
while (stack.length > 0) {
let item = stack.shift();
item.children = treeObj.reduce((list, child) => {
if (<ids match>) {
list.push(child);
}
return list;
}, []).map(child => {
child.parent = item;
stack.unshift(item);
return child;
});
}
return result;
UPDATE
So in »good old JS« and with some improvements:
var stack = treeObj.filter(function (item) {
return item.<parent_id> === item.<child_id> });
var result = [].concat(stack);
while (stack.length > 0) {
var item = stack.shift();
item.children = treeObj.filter(function (child) {
return item.<id> === child.<parent_id>;
});
item.children.forEach(function (child) { stack.unshift(child) });
}
Basically:
find the root(s) and save them on the stack
while.length > 0
shift() the first item from the stack
find all children of that item and unshift them on the stack
Done
Adding a parent property to the items, or removing unneeded one, can be done in the loop. The whole thing can also be done using recursion, but I once ran in the »too much recursion error« by doing such stuff, so I prefer an iterative approach. And of course, instead of .reduce, .filter and .forEach, you can use regular loops, but I prefer a functional style. All in all, no matter how you do it, it is not that difficult, just find the elements to start with and then repeat with all of their children and so on. Withing the while loop all children are found, or, the whole subtree with that element as root.
Good luck!
You're basically trying to convert an array to a n-ary tree.
Entering your original tree function into formatData gives you an array (arr) with the referenceIDs of all objects having SAN001 as parent and another array (subArrs) with all children of with sponerId SAN001.
You then store the arr in subIds, log subIds and subArr and proceed to call format data on subArrs recursively. Then you check on subArr which objects have "SAN001 as predecessor" (which should be all objects at that time) and push that object in subArr. If im not getting it wrong this leads to an infinite loop.
Starting points for improvements:
you're "find" variable does not change, it's hardwired to "SAN001" - this would maybe be okay for your first trip when you're 100% sure that the root object has always this referenceID. But in the second trip, you want to check which objects depend on a second level element, so you need to set find to an corresponding referenceId.
Your subArrs contains all objects depending on SAN001. But in the second and following trips, you don't want to get childrens of SAN001 but child of objects in the subArr. So you need to travers old objects finding children of objects in subArr instead of traversing subArr seeking children of SAN001.
Hope that clear's it up a little bit.
A hint for further research: You're basically trying to "convert an array into a n-ary tree" with javascript.

JavaScript Deep Extend Objects with Arrays

Building on this angular recursive extend topic.
I have slightly modified the version...
var extendDeep = function(dst) {
angular.forEach(arguments, function(obj) {
if (obj !== dst) {
angular.forEach(obj, function(value, key) {
if (dst[key] && angular.isObject(dst[key])) {
extendDeep(dst[key], value);
} else if(!angular.isFunction(dst[key])) {
dst[key] = value;
}
});
}
});
return dst;
};
but found that it does not account for arrays correctly. For example, if I have a object like:
var obj = { zoo: [ 'moose', 'panda' ] };
and then I call like:
deepExtend(obj, {
zoo: [ 'panda' ]
})
at first glance you would expect it to remove the object, however, it actually ends up like:
{ zoo: [ 'panda', 'panda' ] }
now. The expected output would look like:
{ zoo: [ 'panda' ] }
This is a very simple case of course.
I'm not really looking for a 'unique' or a 'merge' like most solutions talk about. I need to add items that are missing from the left, remove items that are on the left but not on the right, and then iterate recursively and extend those objects in place.
There are many different ways to go about this, looking for feedback on a good approach.

Underscore recursive groupBy array of string arrays

I'm trying to combine and group an array with a bunch of flat arrays that contain only strings, no objects.
So my array looks something like this:
var array = [
["MotherNode", "Node1", "ChildNode1", "ChildOfChildNode1"],
["MotherNode", "Node1", "ChildNode2", "ChildOfChildNode2"],
["MotherNode", "Node2", "ChildNode3", "ChildOfChildNode3"],
["MotherNode", "Node2", "ChildNode3", "ChildOfChildNode4"],
["MotherNode", "Node3", "ChildNode4", "ChildOfChildNode5"],
["MotherNode", "Node3", "ChildNode4", "ChildOfChildNode5"]
]
Im doing this in javascript/angularjs and so far I've gathered that the best solution is probably to use underscore.js groupBy/combine methods. However most of the examples that i can find are dealing with arrays of objects where they can group them together by using a value's key. And I'm not good enough with algorithms yet to be able to figure this out on my own.
The array I'm dealing with can have hundreds of values and the result array could get 5-10 levels deep.
The result I'd like by parsing the above array would look something like this:
var result= {
"MotherNode": [{
"Node1":[{
"ChildNode1":"ChildOfChildNode1"
},{
"ChildNode2":"ChildOfChildNode2"
},{
"Node2":[{
"ChildNode3":["ChildOfChildNode3","ChildOfChildNode4"]
},{
"Node3":[{
"ChildNode4":"ChildOfChildNode5"
}
]
}
So does anyone have any clue how this can be done? I'm completely out of ideas.
I solved this using _.reduce grouping wasnt the way to go
var result = _.reduce(array,function(memo, val){
var tmp = memo;
_.each(val, function(fldr){
if(!_.has(tmp, fldr)){
tmp[fldr] = {}
}
tmp = tmp[fldr]
})
return memo
},{})
the end leaf wont be set as a value but it should be easy to change that behavior to whatever suits you use case
{ MotherNode:
{ Node1:
{ ChildNode1: { ChildOfChildNode1: {} },
ChildNode2: { ChildOfChildNode2: {} } },
Node2: { ChildNode3: { ChildOfChildNode3: {}, ChildOfChildNode4: {} } },
Node3: { ChildNode4: { ChildOfChildNode5: {} } } } }

Categories