I have a JSON array of the following format (this data is pulled from mongodb to be displayed as a tree graph on a react-based front-end):
[
{
"name": "7654321",
"children": [
{
"_id": "LjYgocn9PsHhEFbM7",
"accountId": "4343213"
},
{
"_id": "sB2ipCstYnLnHrAuu",
"accountId": "4343271"
},
{
"_id": "JhugmhxS7A57Y34wM",
"accountId": "4343276"
}
]
},
{
"name": "4343213",
"children": [
]
},
{
"name": "4343271",
"children": [
{
"_id": "sie9mtttgdRw7Ktma",
"accountId": "4343279"
}
]
},
{
"name": "4343279",
"children": [
{
"_id": "sie23mtttgdRw7Ktma",
"accountId": "8765345"
}
]
},
{
"name": "4343276",
"children": [
]
}
]
The goal is to re-format (rename and delete some keys) this data to be used in react-tree-graph. From the sample above, output should look like:
[
{
"name": "7654321",
"children": [
{
"name": "4343213"
},
{
"name": "4343271",
"children": [
{
"name": "4343279",
"children": [
{
"name": "8765345"
}
]
}
]
},
{
"name": "4343276"
}
]
}
]
Any help is appreciated!
You could first create a Map that has as keys the name property values, and as corresponding values the (unfinished) result objects. They start off with just the name property.
Then you can iterate the children information in the input, to wire the children into the above mentioned result objects, which can be done efficiently using the name as key in the Map.
Whenever you wire a child object into a parent object, you know that child is not a top-level object in the final result. So starting with all nodes, you would trim that list (a Set) all those nodes that occur in a children array. This will leave you with only the top level nodes, which in its array form represents the desired output.
Implementation:
let data = [{"name": "7654321","children": [{"_id": "LjYgocn9PsHhEFbM7","accountId": "4343213"},{"_id": "sB2ipCstYnLnHrAuu","accountId": "4343271"},{"_id": "JhugmhxS7A57Y34wM","accountId": "4343276"}]},{"name": "4343213","children": []},{"name": "4343271","children": [{"_id": "sie9mtttgdRw7Ktma","accountId": "4343279"}]},{"name": "4343279","children": [{"_id": "sie23mtttgdRw7Ktma","accountId": "8765345"}]},{"name": "4343276","children": []}];
let map = new Map(data.map(({name, children}) => [name, { name }]));
let roots = new Set(map.values());
for (let {name, children} of data) {
if (!children?.length) continue;
map.get(name).children = children.map(({accountId}) => {
let child = map.get(accountId) || { name: accountId };
roots.delete(child);
return child;
});
}
let result = Array.from(roots);
console.log(result);
Related
i have an array structure like below, which has combinedPorts as key with nested array. i can able to iterate and display the properties with .map function in ES6 if all the object has same number of combinedPorts key. But here in the first object the combinedPorts array appears three time whereas in second object the combinedPorts array appears twice. How to iterate the combinedPorts key if it appears different from one object to other.
[
{
"name": "Test Source",
"combinedPorts": [
{
"name": "PortGroup_1",
"templateId": "edfb5b72ec580b129465ea0e8029bad3",
"type": "SourcePorts",
"combinedPorts": [
{
"name": "Source_1",
"templateId": "2355fc02e18cd48c6b487aa8b6f75959",
"type": "SourcePorts",
"combinedPorts": [
{
"name": "Sami_TestSource",
"templateId": "0007ad49ea9b02b309a1248592a01981",
"type": "SourcePorts"
},
],
}
],
}
],
"portGroupInfo": []
},
{
"name": "Test Source",
"combinedPorts": [
{
"name": "PortGroup_1",
"templateId": "edfb5b72ec580b129465ea0e8029bad3",
"type": "SourcePorts",
"combinedPorts": [
{
"name": "Source_1",
"templateId": "2355fc02e18cd48c6b487aa8b6f75959",
"type": "SourcePorts"
}
],
}
],
"portGroupInfo": []
}
]
can someone guide me achieve this using ES6. Thanks in advance.
I would suggest using a recursive approach, this will deal with any level of nesting.
We'd create a function getNestedArrays() that will combine the elements in nested arrays of a given name: arrayName.
At each level, if we find the right array, we'll add the array to our output without any child arrays, then continue walking through the structure.
let input = [ { "name": "Test Source", "combinedPorts": [ { "name": "PortGroup_1", "templateId": "edfb5b72ec580b129465ea0e8029bad3", "type": "SourcePorts", "combinedPorts": [ { "name": "Source_1", "templateId": "2355fc02e18cd48c6b487aa8b6f75959", "type": "SourcePorts", "combinedPorts": [ { "name": "Sami_TestSource", "templateId": "0007ad49ea9b02b309a1248592a01981", "type": "SourcePorts" }, ], } ], } ], "portGroupInfo": [] }, { "name": "Test Source", "combinedPorts": [ { "name": "PortGroup_1", "templateId": "edfb5b72ec580b129465ea0e8029bad3", "type": "SourcePorts", "combinedPorts": [ { "name": "Source_1", "templateId": "2355fc02e18cd48c6b487aa8b6f75959", "type": "SourcePorts" } ], } ], "portGroupInfo": [] } ]
function getNestedArrays(obj, arrayName) {
let result = [];
for(let key in obj) {
if (obj[key] && typeof(obj[key]) === 'object') {
if ((key === arrayName) && Array.isArray(obj[key])) {
// We've found our array, add without child arrays...
result.push(...obj[key].map(({ [arrayName]: x, ...row}) => ({ ...row })))
}
result.push(...getNestedArrays(obj[key], arrayName));
}
}
return result;
}
console.log(input.map(obj => {
obj.combinedPorts = getNestedArrays(obj, 'combinedPorts');
return obj;
}));
.as-console-wrapper { max-height: 100% !important; }
Right now I have an array of object with children that also have an array of objects.
[
{
"code": "mock-code",
"name": "mock-name",
"children": [
{
"code": "mock-child-code",
"name": "mock-child-name",
},
{
"code": "mock-child-code",
"name": "mock-child-name",
},
],
},
{
"code": "mock-code",
"name": "mock-name",
"children": [],
},
{
"code": "mock-code",
"name": "mock-name",
"children": [
{
"code": "mock-code",
"name": "mock-name",
}
],
}
]
I want to extract the children array and concat them to the parent array like below.
[
{
"code": "m1",
"name": "mock-name",
"children": [
{
"code": "mc-1",
"name": "mn-1",
},
{
"code": "mc-2",
"name": "mn-2",
},
],
},
{
"code": "m2",
"name": "mock-name",
"children": [],
},
{
"code": "mm3",
"name": "mock-name",
"children": [
{
"code": "mc-3",
"name": "mn-3",
}
],
}
{
"code": "mc-1",
"name": "mn-1",
},
{
"code": "mc-2",
"name": "mn-2",
},
{
"code": "mc-3",
"name": "mn-3",
}
]
What are someways to do this. I'm currently looping though the child array creating a new array checking if it's not empty. It all seems a bit messy. Is there a clean way to do this?
let fullList = New Array()
parentData.forEach(element => {
if (!!element.children.length) {
fullList.push(element.children);
}
});
return parentData.concat(fullList);
This isn't giving me the desired results since it's adding another array to the parent object but this is where I am at.
const newArray = originalArray.flatMap(element => [element, ...element.children])
This should do it, and as a bonus will preserve the order (parent1, parent1's children, parent2, parent2's children etc.)
Of course, this works if you have only one level of nesting. If you have greater depth level, that would be a bit more complex, probably using Array.prototype.reduce().
This is my array of objects:
I am using vue.js , I need a tree like this to keep the structure of tree view: https://v2.vuejs.org/v2/examples/tree-view.html
[
{
"name": "",
"children": []
},
{
"name": "",
"children": [
{
"name": "Leggi",
"children": []
}
]
},
{
"name": "",
"children": [
{
"name": "Leggi",
"children": [
{
"name": "2010",
"children": []
}
]
}
]
},
{
"name": "",
"children": [
{
"name": "Leggi",
"children": [
{
"name": "2011",
"children": []
}
]
}
]
},
{
"name": "",
"children": [
{
"name": "Titoli",
"children": []
}
]
}
]
I need a function to retrive an object grouped by name with his childrens
{
"name": "",
"children": [
{
"name": "Leggi",
"children": [
{
"name": "2010",
"children": []
},
{
"name": "2011",
"children": []
}
],
"name": "Titoli",
"children": []
}
]
}
I would like to know if there it is a simple way (instead of writing a recursive function), like using lodash or something near it.
Thanks
I think that i have implemented a more readable answer:
const rootTree = [];
const putInTree = (tree, node) => {
let nodeInTree = tree.find(x => x.name === node.name);
if (!nodeInTree) {
nodeInTree = {name: node.name, children: []};
tree.push(nodeInTree);
}
if (node.children[0]) putInTree(nodeInTree.children, node.children[0])
}
nodes.forEach(node => putInTree(rootTree, node));
nodes here is your start array, let me know if this is ok
treeArchive.forEach(element => {
element.children.forEach(father => {
if (result.children.length != 0) {
cicleChildrens(result, father);
function cicleChildrens(padrePrecedente, nuovoPadre){
var brother = padrePrecedente.children.find(x => x.name == nuovoPadre.name);
if (brother != undefined) cicleChildrens(brother, nuovoPadre.children[0]);
else padrePrecedente.children.push(nuovoPadre);
};
}
else result.children.push(father);
});
});
This is currently my working code.. I'm struggling tryng to understand your code #chriss
Try this one:
function getGroupedByName(given) {
let result = given.reduce((a, b) => {
if(!a[b.name]) a[b.name] = [];
a[b.name] = [...a[b.name], ...b.children];
return a;
}, {});
result = Object.keys(result).map(key => ({name: key, children: getByName(result[key])}));
return result;
}
const o = []; // your initial object
getGroupedByName(o, "Leggi")
It is returning it as an array of objects having name and children props, as i am assuming first level can also have multiple different names, not all being ""
It goes first trough all elements in array and groups them into object with structure { name: children } where children is array of all children for same group.
For each children array it preforms same operation, going trough array and flattening it into { name: children } object.
At this moment we have following structure:
{ "": {
Leggi: {...}
}}
When everything is grouped, Object.keys loops trough all keys and breaks it into array where key is name and value children property
I got a json object like this
{
"id": 1,
"name": "A",
"nodes": [
{
"id": 2,
"name": "B",
"nodes": [
{
"id": 3,
"name": "C",
"nodes": []
}
]
}
]
}
If I have as input the id of the object, lets take id: 3, how would I scan the whole three find the object with specific id and then scan upwards to the last parent.
So after the scan is done I know that C has parent B and B has parent A, so I can then print that like A-B-C
all based on me knowing the id of object I want to find parents of.
The above object can be of any lenght and can have many nodes and levels. So anyone has any idea how to traverse up the levels to top level if starting at specific level?
edit:
when I try to parse this
let data = [
{
"id": 1,
"name": "name",
"testing": "something",
"nodes": [
{
"id": 11,
"name": "name",
"testing": "something",
"nodes": []
}
]
},
{
"id": 2,
"name": "name",
"testing": "something",
"nodes": []
}
]
to json object by doing JSON.parse(data) I get an error
SyntaxError: Unexpected token o in JSON at position 1
at JSON.parse (<anonymous>)
Also tried this
let jsonObject = JSON.stringify($scope.data);
jsonObject = JSON.parse(jsonObject);
createTree(jsonObject, null, nodeData.id)
and get different error:
TypeError: obj.nodes is not iterable
Do a basic DFS scan, add parent property along the way, and climb up when node found.
let jsonParsed = JSON.parse(`
{
"id": 1,
"name": "A",
"nodes": [
{
"id": 2,
"name": "B",
"nodes": [
{
"id": 3,
"name": "C",
"nodes": []
}
]
}
]
}
`)
let arr = []
function climbTree(obj) {
arr.unshift(obj.name)
if (obj.parent) {
climbTree(obj.parent)
}
}
function createTree(obj, parent = null, targetId = null) {
obj.parent = parent
if (targetId === obj.id) {
return climbTree(obj)
}
for (let node of obj.nodes) {
createTree(node, obj, targetId)
}
}
createTree(jsonParsed, null, 3)
console.log(arr.join('-'))
For example, I have a JSON array as follow, I will show it on a treelist ,so that user can select the item.Now there is a feature that allowing the user to search the specific text in the list. And the list is a nested list.I hava bind the JSON array to a treelist using HTML and Javascript. Now I should find a good way to search the specific text, and then return the object that include parent node when I find the text.
var allcategories=
[
{
"name": "shoes",
"subcategories": [
{
"name": "man's shoes",
"subcategories": [
{
"name": "sample name"
},
{
"name": "sample name",
"subcategories": []
}
]
},
{
"name": "woman's shoes",
"subcategories": []
}
]
},
{
"name": "cars",
"subcategories": [
{
"name": "cars1",
"subcategories": [
{
"name": "sample name"
},
{
"name": "sample name",
"subcategories": []
}
]
}
]
}
];
So now I want to use the indexOf() function to search the result. If the search is successful, the program should just return the parent category.Anyone have the same question in developing the codes?
As Bergi suggests, you can use a recursive function:
function getCategory(categories, name) {
var category, result;
for (var i=0, iLen=categories.length; i<iLen; i++) {
category = categories[i];
if (category.name == name) {
return category;
} else if (category.subcategories) {
result = getCategory(category.subcategories, name);
if (result) return result;
}
}
// return undefined if category name not found
}
Note that this will return the first subcategory with a matching name, so the name needs to be unique.
#Leeli - you can use this JS lib; DefiantJS (http://defiantjs.com) with which searches in JSON structures becomes a trivial thing. The lib extends the global object JSON with the method "search". With this method, you can search with XPath expressions and it will retusn an array with the matches (empty array if no matches were found). See the example code below.
Here is a working fiddle:
http://jsfiddle.net/hbi99/wXfE6/
var data = [
{
"name": "shoes",
"subcategories": [
{
"name": "man's shoes",
"subcategories": [
{ "name": "heels" },
{ "name": "loafers" }
]
},
{ "name": "woman's shoes" }
]
},
{
"name": "cars",
"subcategories": {
"name": "cars1",
"subcategories": [
{ "name": "Sedan" },
{ "name": "SUV" }
]
}
}
],
res = JSON.search( data, '//*[name="cars1"]/subcategories' );
console.log( res[0].name );
// Sedan
hi here is a jquery function i wrote. since its a associated list in .net the child list is an empty object when there are no child nodes so i have to check is empty instead of simply looking for it to be null. should be backwards compatible though and work as a solution to this question
function findCategory(categories, categoryId)
{
var ret = null;
$.each(categories, function (e, v) {
if (e === categoryId)
ret = v;
else if (!jQuery.isEmptyObject(v.children))
ret = findCategory(v.children, categoryId);
return !ret; //break out of loop when ret not null
});
return ret;
}