Have a data structure with unknown length - javascript

How to print this dynamically?
data = {
"name": "EU",
"size": 10,
"nodes": [
{
"name": "England",
"size": 2,
"nodes": [
{
"name": "Center",
"size": 1,
"nodes": [
{
"name": "main street",
"size": 0.5,
"nodes": []
}
]
},
{
"name": "Vilage",
"size": 1,
"nodes": []
}
]
},
{
"name": "Germany",
"size": 4,
"nodes": []
}
]
}
and I need somehow dynamically print it like this
EU 10
EU - England 2
EU - England - Center 1
EU - England - Center - main street 0.5
EU - England - Vilage 1
EU - Germany 4
This is the code that i have closest what I need
var data = { "name": "EU", "size": 10, "nodes": [ { "name": "England", "size": 2, "nodes": [ { "name": "Center", "size": 1, "nodes": [ { "name": "main street", "size": 0.5, "nodes": [] } ] }, { "name": "Vilage", "size": 1, "nodes": [] } ] }, { "name": "Germany", "size": 4, "nodes": [] } ] }
function printValues(obj) {
for (var key in obj) {
if (typeof obj[key] === "object") {
printValues(obj[key]);
} else {
console.log(obj[key]);
}
}
}
printValues(data)
This is what I get

You were on the right track - you can indeed use recursion to walk the data structure. What you also need is to get the name property and build up a the start of each line from each name encountered. You can ferry that information using a second parameter to the recursive function. Then you need to recursively call the function for each child node and pass all the names encountered so far:
const data = { "name": "EU", "size": 10, "nodes": [ { "name": "England", "size": 2, "nodes": [ { "name": "Center", "size": 1, "nodes": [ { "name": "main street", "size": 0.5, "nodes": [] } ] }, { "name": "Vilage", "size": 1, "nodes": [] } ] }, { "name": "Germany", "size": 4, "nodes": [] } ] }
function printValues(data, prefix = []) {
//make a copy of the prefix instead of mutating it
let location = prefix.concat(data.name);
console.log(location.join(" - "), data.size);
data.nodes.forEach(child => printValues(child, location))
}
printValues(data)

Here what i got
var data = { "name": "EU", "size": 10, "nodes": [ { "name": "England", "size": 2, "nodes": [ { "name": "Center", "size": 1, "nodes": [ { "name": "main street", "size": 0.5, "nodes": [] } ] }, { "name": "Vilage", "size": 1, "nodes": [] } ] }, { "name": "Germany", "size": 4, "nodes": [] } ] }
function printElem(str, element) {
ret = str + " " + element.name + " " + element.size;
console.log(ret);
for (item of element.nodes) {
printElem(str + " " + element.name, item);
}
}
printElem("", data);

Try this -
var data = { "name": "EU", "size": 10, "nodes": [ { "name": "England", "size": 2, "nodes": [ { "name": "Center", "size": 1, "nodes": [ { "name": "main street", "size": 0.5, "nodes": [] } ] }, { "name": "Vilage", "size": 1, "nodes": [] } ] }, { "name": "Germany", "size": 4, "nodes": [] } ] }
function printValues(data, prepend) {
console.log(prepend + data.name + ' ' + data.size);
prepend = prepend + data.name + ' - ';
for (node of data.nodes) {
printValues(node, prepend)
}
}
printValues(data, '')

You could take a closure over the visited names and iterate the nodes.
const
getItems = (parts = []) => ({ name, size, nodes = []}) => {
console.log(`${parts.concat(name).join(' - ')} ${size}`)
nodes.forEach(getItems(parts.concat(name)));
};
var data = { name: "EU", size: 10, nodes: [{ name: "England", size: 2, nodes: [{ name: "Center", size: 1, nodes: [{ name: "main street", size: 0.5, nodes: [] }] }, { name: "Vilage", size: 1, nodes: [] }] }, { name: "Germany", size: 4, nodes: [] }] };
getItems()(data);

One way is to use recursion on the nodes list:
I'm outputting the results with console.log.
Changed thanks to comment by VLAZ.
var data = { "name": "EU", "size": 10, "nodes": [ { "name": "England", "size": 2, "nodes": [ { "name": "Center", "size": 1, "nodes": [ { "name": "main street", "size": 0.5, "nodes": [] } ] }, { "name": "Vilage", "size": 1, "nodes": [] } ] }, { "name": "Germany", "size": 4, "nodes": [] } ] }
function printValues(prefix,obj) {
if (typeof obj !== "object") return;
prefix = prefix?(prefix+" - "+obj.name) : obj.name;
console.log(prefix+" "+obj.size);
for (var key in obj.nodes) {
printValues(prefix,obj.nodes[key]);
}
}
printValues("",data);

Related

typescript: traverse nested array of objects until condition is met

I have nested array like following.
const tree = {
"id": 1,
"name": "mainOrgName",
"children": [
{
"id": 10,
"name": "East Region",
"children": [
{
"id": 11,
"name": "test east sub 1",
"children": [
{
"id": 12,
"name": "sub 22 sub 1",
"children": [
{
"id": 15,
"name": "sub 333 of sub ",
"children": [
{
"id": 16,
"name": "sub 4444",
"children": []
}
]
}
]
}
]
}
]
},
{
"id": 13,
"name": "west region",
"children": [
{
"id": 14,
"name": "test west sub 1",
"children": []
}
]
}
]
}
I need to traverse through tree.children array to get all id and name of sub arrays and its children until we didn't find children array empty. (Note: children array may be empty or may have further levels)
I need result like follows
Expected result
[
{
"name": "East Region",
"value": 10,
"selected": false
},
{
"name": "test east sub 1",
"value": 11,
"selected": false
},
{
"name": "sub 22 sub 1",
"value": 12,
"selected": false
},
{
"name": "sub 333 of sub",
"value": 15,
"selected": false
},
{
"name": "sub 4444",
"value": 16,
"selected": false
},
{
"name": "west region",
"value": 13,
"selected": false
},
{
"name": "test west sub 1",
"value": 14,
"selected": false
},
]
I tried following
const candidates = tree.children.map(org => ({name: org.name, value: org.id, selected: false}));
but it gives me following
[
{
"name": "East Region",
"value": 10,
"selected": false
},
{
"name": "west region",
"value": 13,
"selected": false
}
]
I am trying to get that but not sure how I can put condition that traverse until children is empty and push required fields in final array in required format. May need recursive/call back functions but not sure how I can use that.
Please help to get expected result. Thanks
Try this,
const tree = {
"id": 1,
"name": "mainOrgName",
"children": [
{
"id": 10,
"name": "East Region",
"children": [{
"id": 11,
"name": "test east sub 1",
"children": [{
"id": 12,
"name": "sub 22 sub 1",
"children": [{
"id": 15,
"name": "sub 333 of sub ",
"children": [{
"id": 16,
"name": "sub 4444",
"children": []
}]
}]
}]
}]
},
{
"id": 13,
"name": "west region",
"children": [{
"id": 14,
"name": "test west sub 1",
"children": []
}]
}
]
}
let items = []
let result = lootIt(tree.children)
console.log(result)
function lootIt (arr) {
for(let i = 0 ; i< arr.length ; i++){
let obj = {}
obj['name'] = arr[i]['name']
obj['value'] = arr[i]['id']
obj['selected'] = false
items.push(obj)
if(arr[i].children !== 0){
lootIt(arr[i].children)
}
}
return items
}
You can use recursion to do it
const tree = {
"id": 1,
"name": "mainOrgName",
"children": [
{
"id": 10,
"name": "East Region",
"children": [
{
"id": 11,
"name": "test east sub 1",
"children": [
{
"id": 12,
"name": "sub 22 sub 1",
"children": [
{
"id": 15,
"name": "sub 333 of sub ",
"children": [
{
"id": 16,
"name": "sub 4444",
"children": []
}
]
}
]
}
]
}
]
},
{
"id": 13,
"name": "west region",
"children": [
{
"id": 14,
"name": "test west sub 1",
"children": []
}
]
}
]
}
function mapTree(children){
let result =[]
for(c of children){
result.push({name: c.name, value: c.id, selected: false})
result = result.concat(mapTree(c.children))
}
return result
}
console.log(mapTree(tree.children))

Filter nested objects by property value recursively and keep array structure

I have the following array of objects with nested elements in the children property. I need to get objects by their id if the id matches.
[
{
"id": 10,
"name": "Scenarios",
"value": null,
"children": [
{
"id": 12,
"name": "Scenario status",
"value": null,
"children": []
}
]
},
{
"id": 11,
"name": "Forecast source",
"value": null,
"children": []
},
{
"id": 16787217,
"name": "Item#Cust",
"value": null,
"children": [
{
"id": 16787230,
"name": "Customer",
"value": null,
"children": [
{
"id": 16787265,
"name": "Site",
"value": null,
"children": []
},
{
"id": 16787291,
"name": "Commercial Network",
"value": null,
"children": []
},
{
"id": 16787296,
"name": "Distribution Site",
"value": null,
"children": []
}
]
},
{
"id": 16787245,
"name": "Item#Site",
"value": null,
"children": [
{
"id": 16787266,
"name": "Family#Warehouse",
"value": null,
"children": []
}
]
},
{
"id": 16787254,
"name": "Item",
"value": null,
"children": [
{
"id": 16787260,
"name": "Family",
"value": null,
"children": [
{
"id": 16787264,
"name": "Product line",
"value": null,
"children": []
}
]
},
{
"id": 16787261,
"name": "Group 1",
"value": null,
"children": []
}
]
}
]
},
{
"id": 16787267,
"name": "Supplier",
"value": null,
"children": []
},
{
"id": 16787297,
"name": "SKU",
"value": null,
"children": []
}
]
If no match on root element is found, the function should lookup its children to see if there is a match, and the first children match should be pushed on the root level.
For exemple, I have a list of ids: [12, 16787217, 16787245, 16787266]
The function should return only objects where ids matches while keeping hierarchy if matching parent id have matching children id, so it should return this:
[
{
"id": 12,
"name": "Scenario status",
"value": null,
"children": []
},
{
"id": 16787217,
"name": "Item#Cust",
"value": null,
"children": [
{
"id": 16787245,
"name": "Item#Site",
"value": null,
"children": [
{
"id": 16787266,
"name": "Family#Warehouse",
"value": null,
"children": []
}
]
}
]
}
]
As for now, I can get only the first level elements if they are found with this function:
filterArray(array: Array<any>, ids: Array<number>) {
array = array.filter(el => ids.includes(el.id));
for (let i = 0; i < array.length; i++) {
this.filterArray(array[i].children, ids);
}
console.log(array);
}
Anyone have an idea on how to achieve this?
You could reduce the array and take the nodes with found id and children or only the children at the base level.
const
find = (r, { children = [], ...o }) => {
children = children.reduce(find, []);
if (ids.includes(o.id)) r.push({ ...o, children });
else if (children.length) r.push(...children);
return r;
},
data = [{ id: 10, name: "Scenarios", value: null, children: [{ id: 12, name: "Scenario status", value: null, children: [] }] }, { id: 11, name: "Forecast source", value: null, children: [] }, { id: 16787217, name: "Item#Cust", value: null, children: [{ id: 16787230, name: "Customer", value: null, children: [{ id: 16787265, name: "Site", value: null, children: [] }, { id: 16787291, name: "Commercial Network", value: null, children: [] }, { id: 16787296, name: "Distribution Site", value: null, children: [] }] }, { id: 16787245, name: "Item#Site", value: null, children: [{ id: 16787266, name: "Family#Warehouse", value: null, children: [] }] }, { id: 16787254, name: "Item", value: null, children: [{ id: 16787260, name: "Family", value: null, children: [{ id: 16787264, name: "Product line", value: null, children: [] }] }, { id: 16787261, name: "Group 1", value: null, children: [] }] }] }, { id: 16787267, name: "Supplier", value: null, children: [] }, { id: 16787297, name: "SKU", value: null, children: [] }],
ids = [12, 16787217, 16787245, 16787266],
result = data.reduce(find, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could also try this
let data = [
{
"id": 10,
"name": "Scenarios",
"value": null,
"children": [
{
"id": 12,
"name": "Scenario status",
"value": null,
"children": []
}
]
},
{
"id": 11,
"name": "Forecast source",
"value": null,
"children": []
},
{
"id": 16787217,
"name": "Item#Cust",
"value": null,
"children": [
{
"id": 16787230,
"name": "Customer",
"value": null,
"children": [
{
"id": 16787265,
"name": "Site",
"value": null,
"children": []
},
{
"id": 16787291,
"name": "Commercial Network",
"value": null,
"children": []
},
{
"id": 16787296,
"name": "Distribution Site",
"value": null,
"children": []
}
]
},
{
"id": 16787245,
"name": "Item#Site",
"value": null,
"children": [
{
"id": 16787266,
"name": "Family#Warehouse",
"value": null,
"children": []
}
]
},
{
"id": 16787254,
"name": "Item",
"value": null,
"children": [
{
"id": 16787260,
"name": "Family",
"value": null,
"children": [
{
"id": 16787264,
"name": "Product line",
"value": null,
"children": []
}
]
},
{
"id": 16787261,
"name": "Group 1",
"value": null,
"children": []
}
]
}
]
},
{
"id": 16787267,
"name": "Supplier",
"value": null,
"children": []
},
{
"id": 16787297,
"name": "SKU",
"value": null,
"children": []
}
]
let ids = [12, 16787217, 16787245, 16787266]
let filterArray = (mArray, ids) => {
let result = []
for (const x of mArray) {
if (ids.includes(x.id)) {
let element = {...x, children: []}
let childrenFounds = []
if (x.children.length) {
childrenFounds = filterArray(x.children, ids)
if (childrenFounds.length) element.children.push(...childrenFounds)
}else {
delete element.children
}
result.push(element)
} else if (x.children.length) {
let found = filterArray(x.children, ids)
if (found.length) result.push(...found)
}
}
return result
}
let res = filterArray(data, ids)
console.log('res', res)

Get all parents ids from id list in nested object in javascript

So I have an array of objects which have all nested children property. It is in front-end a treeview, which should expand the nodes until selected ones, for each id in a list. To be able to do this, I have to get all the parents ids for each selected id from the list.
For example, my list of checkedKeys ids:
[16787217, 16787245, 16787266, 16787270, 16787272, 16787265, 16787264]
All the checked items represents the ids from the list object:
My object list looks like this:
[
{
"id": 11,
"name": "Forecast source",
"value": null,
"children": []
},
{
"id": 2,
"name": "Item Type",
"value": null,
"children": []
},
{
"id": 16787217,
"name": "Item#Cust",
"value": null,
"children": [
{
"id": 16787230,
"name": "Customer",
"value": null,
"children": [
{
"id": 16787291,
"name": "Commercial Network",
"value": null,
"children": []
},
{
"id": 16787296,
"name": "Distribution Site",
"value": null,
"children": []
},
{
"id": 16787265,
"name": "Site",
"value": null,
"children": []
}
]
},
{
"id": 16787254,
"name": "Item",
"value": null,
"children": [
{
"id": 16787294,
"name": "ABC (Regular)",
"value": null,
"children": []
},
{
"id": 16787273,
"name": "ABC (U)",
"value": null,
"children": []
},
{
"id": 16787278,
"name": "ABC (€)",
"value": null,
"children": []
},
{
"id": 16787290,
"name": "Class",
"value": null,
"children": []
},
{
"id": 16787260,
"name": "Family",
"value": null,
"children": [
{
"id": 16787264,
"name": "Product line",
"value": null,
"children": []
}
]
},
{
"id": 16787263,
"name": "Flavour",
"value": null,
"children": []
},
{
"id": 16787262,
"name": "Format",
"value": null,
"children": []
},
{
"id": 16787261,
"name": "Group 1",
"value": null,
"children": []
},
{
"id": 16787292,
"name": "ProdGroup",
"value": null,
"children": []
},
{
"id": 16787293,
"name": "Recipe",
"value": null,
"children": []
},
{
"id": 16787288,
"name": "Sale status",
"value": null,
"children": []
}
]
},
{
"id": 16787245,
"name": "Item#Site",
"value": null,
"children": [
{
"id": 16787266,
"name": "Family#Warehouse",
"value": null,
"children": []
}
]
}
]
},
{
"id": 3,
"name": "Lead Time",
"value": null,
"children": []
},
{
"id": 5,
"name": "Levels",
"value": null,
"children": []
},
{
"id": 16787268,
"name": "N1",
"value": null,
"children": [
{
"id": 16787269,
"name": "N2",
"value": null,
"children": [
{
"id": 16787270,
"name": "N3",
"value": null,
"children": [
{
"id": 16787271,
"name": "N4",
"value": null,
"children": [
{
"id": 16787272,
"name": "N5",
"value": null,
"children": [
{
"id": 33564497,
"name": "N6",
"value": null,
"children": [
{
"id": 33564498,
"name": "N7",
"value": null,
"children": [
{
"id": 33564499,
"name": "N8",
"value": null,
"children": [
{
"id": 33564500,
"name": "N9",
"value": null,
"children": []
}
]
}
]
}
]
}
]
}
]
}
]
}
]
}
]
},
{
"id": 16787286,
"name": "Op set",
"value": null,
"children": []
}
]
So my problem is that I can't figure out how to get all parents nodes ids recursively until the root level and push them into the expandedKeys array.
I tried to implement a function like this:
this.list.forEach(el => {
this.setExpandedNodes(el);
});
setExpandedNodes(node: PropertyNode) {
if (node.children) {
node.children.forEach(chld => {
this.checkedKeys.forEach(key => {
if (chld.id === key) {
this.expandedKeys.push(node.id);
} else if (chld.children) {
chld.children.forEach(grChld => {
this.setExpandedNodes(grChld);
});
}
});
});
}
}
But I can't figure out how to get all parent ids starting from each selected id until the root level. Anyone have an idea?
Thanks to this answer I managed to do it.
getPath(model, id) {
let path,
item = model.id ;
if (!model || typeof model !== 'object') {
return;
}
if (model.id === id) {
return [item];
}
(model.children || []).some(child => (path = this.getPath(child, id)));
return path && [item, ...path];
}
setExpandedKeys() {
if (this.checkedKeys && this.checkedKeys.length > 0) {
this.checkedKeys.forEach(k => {
this.list.forEach(
mt => {
const result = this.getPath(mt, k);
if (result) {
this.expandedKeys.push(...result);
}
}
);
});
}
}
this.setExpandedKeys();
I have now all the parents ids until the root.
A function that returns an array of parent objects may look like this:
Using tree-model-js:
const getParentsById = (id, data) => {
var TreeModel = require('tree-model')
const tree = new TreeModel()
let path
for (let item of data) {
const root = tree.parse(item)
root.walk(function (node) {
// Halt the traversal by returning false
if (node.model.id === id) {
path = node.getPath()
return false;
}
});
}
path.pop()
return path.map(item => item.model)
}
Without using tree-model-js:
const getParentsById = (id, data) => {
const isFoundChild = (id, data, parents) => {
if (data.find(item => item.id == id)) {
return true;
}
else {
for (let item of data) {
if (item.children.length)
if (isFoundChild(id, item.children)) {
parents.push(item);
return true;
}
}
return false;
}
}
const parents = [];
if (data.find(item => item.id == id))
return [];
else {
for (let item of data) {
if (item.children.length)
if (isFoundChild(id, item.children, parents)) {
parents.push(item);
return parents;
}
}
}
}
Next, it is enough to apply map() to the result:
let parentsId = getParentsById(16787296, data).map(item => item.id)

Need to modify BE response to expected response at FE

BE Response:- which I am getting from Backend Service
{
"data": {
"type": "AnyType",
"resources": [
{
"id": 1,
"treeId": "1",
"name": "name1",
"description": "description1",
"children": [
{
"id": 3,
"treeId": "1-3",
"name": "subName1",
"description": "subDescription1",
"children": [
{
"id": 6,
"treeId": "1-3-6",
"name": "subSubName1",
"description": "subSubDesc1",
"children": []
}
]
}
]
},
{
"id": 2,
"treeId": "2",
"name": "name2",
"description": "description2",
"children": [
{
"id": 7,
"treeId": "2-7",
"name": "subName2",
"description": "subDescription2",
"children": []
}
]
}
]
}
}
But I need to modify this response to as below on FE
Expected Response:- means I need to join name and description field text to one(in name field ) as below:-
{
"data": {
"type": "AnyType",
"resources": [
{
"id": 1,
"treeId": "1",
"name": "name1-description1",
"description": "description1",
"children": [
{
"id": 3,
"treeId": "1-3",
"name": "subName1-subDescription1",
"description": "subDescription1",
"children": [
{
"id": 6,
"treeId": "1-3-6",
"name": "subSubName1-subSubDesc1",
"description": "subSubDesc1",
"children": []
}
]
}
]
},
{
"id": 2,
"treeId": "2",
"name": "name2-description2",
"description": "description2",
"children": [
{
"id": 7,
"treeId": "2-7",
"name": "subName2-subDescription2",
"description": "subDescription2",
"children": []
}
]
}
]
}
}
there could be n number of children of each object and children can have an array of objects.
What I have done:- I am able to change the very first name but not children name
let resDataArry = [];
let descData: DynamicResource;
response.forEach((x, index) => {
const descName = x.name + ' ' + x.description;
descData = { ...tree.resources[index], name: descName };
resDataArry.push(descData);
});
return resDataArry;
Please help.
You can use nested Array#forEach to access children array and then concatenate the name and description together.
let data = {
"data": {
"type": "AnyType",
"resources": [
{
"id": 1,
"treeId": "1",
"name": "name1",
"description": "description1",
"children": [
{
"id": 3,
"treeId": "1-3",
"name": "subName1",
"description": "subDescription1",
"children": [
{
"id": 6,
"treeId": "1-3-6",
"name": "subSubName1",
"description": "subSubDesc1",
"children": []
}
]
}
]
},
{
"id": 2,
"treeId": "2",
"name": "name2",
"description": "description2",
"children": [
{
"id": 7,
"treeId": "2-7",
"name": "subName2",
"description": "subDescription2",
"children": []
}
]
}
]
}
}
data.data.resources.forEach(function(item){
item.name = item.name + ' ' + item.description;
item.children.forEach(function(child){
child.name = child.name + ' ' + child.description;
});
});
console.log(data);

linqjs group by multiple levels

I am trying to figure out how to group by at multiple levels:
Given the following input JSON:
[
{
"GroupingMajor": "Fruit",
"Grouping": "Banana",
"Description": "10 Bananas",
"Price": 20609.82,
},
{
"GroupingMajor": "Fruit",
"Grouping": "Apple",
"Description": "13 Apples",
"Price": 4567.98,
},
{
"GroupingMajor": "Dairy",
"Grouping": "Cheese",
"Description": "1 KG",
"Price": 76456.76,
}
]
I would like to create output of type:
{
"name": "root",
"children": [
{
"name": "Fruit",
"children": [
{
{"name": "Apple", "price": 4567.98},
{"name": "Banana", "price": 20609.82}
}]
},
{
"name": "Dairy",
"children": [
{
{"name": "Cheese", "price": 76456.76}
}]
}]
}
That is, data is grouped by "GroupingMajor" and then "Grouping". "Grouping" being a child of the parent "GroupingMajor" category. How can I generate this output using linq.js (JavaScript)?
Here is some JavaScript that I've attempted:
var data = Enumerable.From(dataFood)
.GroupBy(
function (e) {
return {
groupmajor: e.GroupingMajor,
groupminor: e.Grouping
}},
function (e) {
return {
groupmajor: e.GroupingMajor,
groupminor: e.Grouping,
name: e.GroupingMajor,
price: e.Price,
}
},
function (key, g) {
return {
group: key.groupmajor,
groupminor: key.groupminor,
name: g.name,
price: g.price
}
})
.ToArray();
I am not getting a correct grouping and I have no idea how to generate a sub-group.
Any help is appreciated.
Here's how you could construct such a query:
var query = Enumerable.From(data)
.GroupBy(
"$.GroupingMajor", // key
"{ name: $.Grouping, price: $.Price }", // item
"{ name: $, children: $$.ToArray() }" // result
)
.ToArray();
var result = {
name: 'root',
children: query
};
Which would yield the following result:
{
"name": "root",
"children": [
{
"name": "Fruit",
"children": [
{
"name": "Banana",
"price": 20609.82
},
{
"name": "Apple",
"price": 4567.98
}
]
},
{
"name": "Dairy",
"children": [
{
"name": "Cheese",
"price": 76456.76
}
]
}
]
}

Categories