I need help to find all combined possibilities from a tree
I readed a lot of documentation about cartesian product, tried a lot of things but none of them seems to work properly...
This is my tree
var data = [
{
"id": 5,
"name": "Support papier",
"type": "filter",
"children": [
{
"id": 24,
"name": "60 g/m² papier mat",
"type": "value",
"children": []
},
{
"id": 7,
"name": "90 g/m² papier couché",
"type": "value",
"children": [
{
"id": 8,
"name": "Propriété papier",
"type": "filter",
"children": [
{
"id": 18,
"name": "Papier mat",
"type": "value",
"children": [],
},
{
"id": 60,
"name": "Papier brillant",
"type": "value",
"children": [],
}
]
}
],
}
]
}
]
And this is my expected result :
[
[
{id: 5, name:"support papier", type: "filter"},
{id: 24, name:"60 g/m² papier mat", type: "value"},
],
[
{id: 5, name:"support papier", type: "filter"},
{id: 7, name:"90 g/m² papier mat", type: "value"},
{id: 8, name:"Propriété papier", type: "filter"},
{id: 18, name:"Papier mat", type: "value"},
],
[
{id: 5, name:"support papier", type: "filter"},
{id: 7, name:"90 g/m² papier mat", type: "value"},
{id: 8, name:"Propriété papier", type: "filter"},
{id: 60, name:"Papier brillant", type: "value"},
]
]
Of course, every empty array can be populated... :)
Thx for your help :)
You could get each level and map the next lower level to the result set.
Now, what is it doing?
The first part of collecting data is just getting all nodes to the end of the children's objects.
The other part is using a cartesian product for children which object has
type === 'value'
This works in two steps
Collect all items by getting the data and map these items with the actual object.
Create a cartesian product from an array of items.
The rest is just either pushing a new array with parts and adding the actual object (without children) or if no children are available, only the actual object in an array.
function getData(array) {
return array.reduce((r, { children, ...o }) => {
if (children.length) {
var parts = o.type === 'value'
? children
.map(({ children = [], ...p }) => getData(children).map(q => [p, ...q]))
.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []))
: getData(children);
r.push(...parts.map(q => [o, ...q]));
} else {
r.push([o]);
}
return r;
}, []);
}
var data = [{ id: 5, name: "Support papier", type: "filter", children: [{ id: 24, name: "60 g/m² papier mat", type: "value", children: [{ id: 9, name: "Finition", type: "filter", children: [{ id: 19, name: "Sans finition", type: "value", children: [] }, { id: 20, name: "Vernis anti UV", type: "value", children: [] }] }, { id: 8, name: "Propriété papier", type: "filter", children: [{ id: 60, name: "Papier brillant", type: "value", children: [] }, { id: 18, name: "Papier mat", type: "value", children: [] }] }] }, { id: 7, name: "90 g/m² papier couché", type: "value", children: [{ id: 8, name: "Propriété papier", type: "filter", children: [{ id: 18, name: "Papier mat", type: "value", children: [] }, { id: 60, name: "Papier brillant", type: "value", children: [] }] }] }] }],
result = getData(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
i have following example array(object):
[
{
"id": 1,
"name": "selling",
"detail": [
{
"id": 11,
"name": "sale-report",
"detail": [
{ "id": 111, "name": "sale-report1", "detail": [] },
{ "id": 112, "name": "sale-report2", "detail": [] }
]
}
]
},
{
"id": 2,
"name": "webstore",
"detail": [
{
"id": 11,
"name": "sale-report",
"detail": [
{ "id": 111, "name": "webstore-report1", "detail": [] },
{ "id": 112, "name": "webstore-report2", "detail": [] }
]
}
]
},
{
"id": 2,
"name": "setting",
"detail": [
{
"id": 11,
"name": "general",
"detail": [
{ "id": 111, "name": "setting-general1", "detail": [] },
{ "id": 112, "name": "setting-general2", "detail": [] }
]
}
]
}
]
how to change the array with new format like this
[
{
"id": 1,
"name": "selling",
},
{
"id": 11,
"name": "sale-report"
},
{ "id": 111, "name": "sale-report1" },
{ "id": 112, "name": "sale-report2" },
{
"id": 2,
"name": "webstore",
},
{
"id": 11,
"name": "sale-report",
},
{ "id": 111, "name": "webstore-report1" },
{ "id": 112, "name": "webstore-report2" },
{
"id": 2,
"name": "setting",
},
{
"id": 11,
"name": "general",
},
{ "id": 111, "name": "setting-general1" },
{ "id": 112, "name": "setting-general2" }
]
with the condition that if there is a key "detail" inside object in the branch, it will be mapped as well (assuming unlimited key "detail" inside object inside array)
note: content of detail will be same as parent, but different value
thanks in advance
i tried mapping mannualy with foreach, but i cant figure out if detail key with array(object) has unlimited nesting
Simply, use recursion to do this.
Here is the working demo-
let input = [
{
id: 1,
name: "selling",
detail: [
{
id: 11,
name: "sale-report",
detail: [
{ id: 111, name: "sale-report1", detail: [] },
{ id: 112, name: "sale-report2", detail: [] }
]
}
]
},
{
id: 2,
name: "webstore",
detail: [
{
id: 11,
name: "sale-report",
detail: [
{ id: 111, name: "webstore-report1", detail: [] },
{ id: 112, name: "webstore-report2", detail: [] }
]
}
]
},
{
id: 2,
name: "setting",
detail: [
{
id: 11,
name: "general",
detail: [
{ id: 111, name: "setting-general1", detail: [] },
{ id: 112, name: "setting-general2", detail: [] }
]
}
]
}
];
let result = [];
function parseData(arr) {
arr.forEach((item) => {
result.push({
id: item.id || null,
name: item.name || null
});
if (item.detail && item.detail.length) {
parseData(item.detail);
}
});
return result;
}
let output = parseData(input);
console.log(output);
You could destructure the objects and take detail out of the object and map the rest object and the results of nested details array as flat array with a recursive function.
const
flat = (array = []) => array.flatMap(({ detail, ...rest }) => [
rest,
...flat(detail)
]),
data = [{ id: 1, name: "selling", detail: [{ id: 11, name: "sale-report", detail: [{ id: 111, name: "sale-report1", detail: [] }, { id: 112, name: "sale-report2", detail: [] }] }] }, { id: 2, name: "webstore", detail: [{ id: 11, name: "sale-report", detail: [{ id: 111, name: "webstore-report1", detail: [] }, { id: 112, name: "webstore-report2", detail: [] }] }] }, { id: 2, name: "setting", detail: [{ id: 11, name: "general", detail: [{ id: 111, name: "setting-general1", detail: [] }, { id: 112, name: "setting-general2", detail: [] }] }] }],
result = flat(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
A non recursive method of doing this could look something like the following:
const data = [{"id":1,"name":"selling","detail":[{"id":11,"name":"sale-report","detail":[{"id":111,"name":"sale-report1","detail":[]},{"id":112,"name":"sale-report2","detail":[]}]}]},{"id":2,"name":"webstore","detail":[{"id":11,"name":"sale-report","detail":[{"id":111,"name":"webstore-report1","detail":[]},{"id":112,"name":"webstore-report2","detail":[]}]}]},{"id":2,"name":"setting","detail":[{"id":11,"name":"general","detail":[{"id":111,"name":"setting-general1","detail":[]},{"id":112,"name":"setting-general2","detail":[]}]}]}];
const extract = (data) => {
const result = [];
const queue = [...data];
while(queue.length > 0) {
const { detail, ...rest } = queue.shift();
result.push(rest);
queue.unshift(...detail);
}
return result;
};
console.log(extract(data));
I've been searching for a basic javascript algorithm for this problem but was unable to find one and was unable to create it from scratch.
For example, the node with most number direct and indirect children under him.
So for the data structure below, I would expect node '12' to be returned as it has more decedents than node '11'
tree = {
"id": 1,
"children": [
{
"id": 11,
"children": [
{
"id": 111,
"children": []
},
{
"id": 112,
"children": [
{
"id": 1121,
"children": []
},
{
"id": 1122,
"children": []
}
]
}
]
},
{
"id": 12,
"children": [
{
"id": 121,
"children": []
},
{
"id": 122,
"children": [
{
"id": 8888,
"children": []
},
{
"id": 5555,
"children": [
{
"id": 6666,
"children": []
},
{
"id": 121212,
"children": []
}
]
}
]
}
]
}
]
};
would appreciate any help, as I'm stumped.
You could map the dept for each root children and reduce the array by taking the one with most children.
const
getC = ({ children }) => children?.reduce((sum, node) => sum + getC(node), 1) || 1,
findNodeWithMostChildren = node => node.children
.map((node) => [node, getC(node)])
.reduce((a, b) => b[1] > a[1] ? b : a)
[0],
tree = { id: 1, children: [{ id: 11, children: [{ id: 111, children: [] }, { id: 112, children: [{ id: 1121, children: [] }, { id: 1122, children: [] }] }] }, { id: 12, children: [{ id: 121, children: [] }, { id: 122, children: [{ id: 8888, children: [] }, { id: 5555, children: [{ id: 6666, children: [] }, { id: 121212, children: [] }] }] }] }] };
console.log(findNodeWithMostChildren(tree));
.as-console-wrapper { max-height: 100% !important; top: 0; }
I want to create a JavaScript method that transforms a Json ‘treeView’ object. My starting object has a structure with duplicated branches:
Example of basic data:
- ROOT
- 01
- 011
- 0111
- 01
- 011
- 0112
- 01
- 011
- 0113
- 01
- 012
- 0121
- 01
- 012
- 0122
- 01
- 012
- 0123
- 01
- 013
- 0131
- 01
- 013
- 0132
- 01
- 013
- 0133
I would like the method to group all the levels with an identical code.
And sort the items alphabetically.
Example of what the method should return:
- ROOT
- 01
- 011
- 0111
- 0112
- 0113
- 012
- 0121
- 0122
- 0123
- 013
- 0131
- 0132
- 0133
Example of basic data (Json):
[
{
"id": 0,
"name": "ROOT",
"children": [
{
"name": "01",
"id": 3,
"children": [
{
"name": "013",
"id": 2,
"children": [
{
"name": "0131",
"id": 1
}
]
}
]
},
{
"name": "01",
"id": 6,
"children": [
{
"name": "011",
"id": 5,
"children": [
{
"name": "0112",
"id": 4
}
]
}
]
},
{
"name": "01",
"id": 9,
"children": [
{
"name": "011",
"id": 8,
"children": [
{
"name": "0111",
"id": 7
}
]
}
]
},
{
"name": "01",
"id": 12,
"children": [
{
"name": "013",
"id": 11,
"children": [
{
"name": "0132",
"id": 10
}
]
}
]
},
{
"name": "01",
"id": 15,
"children": [
{
"name": "013",
"id": 14,
"children": [
{
"name": "0133",
"id": 13
}
]
}
]
},
{
"name": "01",
"id": 18,
"children": [
{
"name": "011",
"id": 17,
"children": [
{
"name": "0113",
"id": 16
}
]
}
]
},
{
"name": "01",
"id": 21,
"children": [
{
"name": "012",
"id": 20,
"children": [
{
"name": "0121",
"id": 19,
}
]
}
]
},
{
"name": "01",
"id": 24,
"children": [
{
"name": "012",
"id": 23,
"children": [
{
"name": "0122",
"id": 22
}
]
}
]
},
{
"name": "01",
"id": 27,
"children": [
{
"name": "012",
"id": 26,
"children": [
{
"name": "0123",
"id": 25
}
]
}
]
}
]
}
]
Example of what the method should return (json):
[
{
"id": 0,
"name": "ROOT",
"children": [
{
"name": "01",
"id": 3,
"children": [
{
"name": "011",
"id": 5,
"children": [
{
"name": "0111",
"id": 7
},
{
"name": "0112",
"id": 4
},
{
"name": "0113",
"id": 16
}
]
},
{
"name": "012",
"id": 20,
"children": [
{
"name": "0121",
"id": 19,
},
{
"name": "0122",
"id": 22
},
{
"name": "0123",
"id": 25
}
]
},
{
"name": "013",
"id": 2,
"children": [
{
"name": "0131",
"id": 1
},
{
"name": "0132",
"id": 10
},
{
"name": "0133",
"id": 13
}
]
}
]
},
]
}
]
I think a recursive method would be the most appropriate, but I don't have too many ideas.
The keyword you are looking for might be trie
But without much theory we can just write some code already
The idea is to consider your output as a tree.
Whenever you access a child of your data, you create a node corresponding to that child in the tree if it does not exists yet
Then your recurse, and in-fine you just have to get back your tree-like structure
const data = [{"id":0,"name":"ROOT","children":[{"name":"01","id":3,"children":[{"name":"013","id":2,"children":[{"name":"0131","id":1}]}]},{"name":"01","id":6,"children":[{"name":"011","id":5,"children":[{"name":"0112","id":4}]}]},{"name":"01","id":9,"children":[{"name":"011","id":8,"children":[{"name":"0111","id":7}]}]},{"name":"01","id":12,"children":[{"name":"013","id":11,"children":[{"name":"0132","id":10}]}]},{"name":"01","id":15,"children":[{"name":"013","id":14,"children":[{"name":"0133","id":13}]}]},{"name":"01","id":18,"children":[{"name":"011","id":17,"children":[{"name":"0113","id":16}]}]},{"name":"01","id":21,"children":[{"name":"012","id":20,"children":[{"name":"0121","id":19}]}]},{"name":"01","id":24,"children":[{"name":"012","id":23,"children":[{"name":"0122","id":22}]}]},{"name":"01","id":27,"children":[{"name":"012","id":26,"children":[{"name":"0123","id":25}]}]}]}]
const makeTree = root => {
const toTree = (trieNode, node) => {
trieNode.children = trieNode.children || []
let trieNodeChild = trieNode.children.find(child => child.name === node.name)
if (!trieNodeChild) {
trieNodeChild = { name: node.name, id: node.id }
trieNode.children.push(trieNodeChild)
}
if (!node.children) return
node.children.forEach(child => toTree(trieNodeChild, child))
return trieNode
}
return toTree({}, root)
}
console.time('a')
console.log(JSON.stringify(makeTree(data[0]), null, 2))
console.timeEnd('a')
Note that it is not very efficient because searching in array is slow
A slightly improved version is to store the trieNodes in a map(name => trieNode)
const makeTree2 = root => {
const cache = new Map
const toTree = (trieNode, node) => {
trieNode.children = trieNode.children || []
let trieNodeChild = cache.get(node.name)
if (!trieNodeChild) {
trieNodeChild = { name: node.name, id: node.id }
trieNode.children.push(trieNodeChild)
cache.set(node.name, trieNodeChild)
}
if (!node.children) return
node.children.forEach(child => toTree(trieNodeChild, child))
return trieNode
}
return toTree({}, root)
}
console.time('a2')
console.log(JSON.stringify(makeTree2(data[0]), null, 2))
console.timeEnd('a2')
On my potatoe, first code takes 5ms, second takes 0.3ms. Does not matter much for this data, but order of complexity is apparent.
Here is an interative solution using object-scan
We traverse the tree and remember the nodes that we have seen. If we encounter a node we have seen previously, we merge its children into the previously seen node and delete the node itself.
Note that different ids are picked, since we have to traverse the tree in "delete-safe" order.
// const objectScan = require('object-scan');
const myTree = [{ id: 0, name: 'ROOT', children: [{ name: '01', id: 3, children: [{ name: '013', id: 2, children: [{ name: '0131', id: 1 }] }] }, { name: '01', id: 6, children: [{ name: '011', id: 5, children: [{ name: '0112', id: 4 }] }] }, { name: '01', id: 9, children: [{ name: '011', id: 8, children: [{ name: '0111', id: 7 }] }] }, { name: '01', id: 12, children: [{ name: '013', id: 11, children: [{ name: '0132', id: 10 }] }] }, { name: '01', id: 15, children: [{ name: '013', id: 14, children: [{ name: '0133', id: 13 }] }] }, { name: '01', id: 18, children: [{ name: '011', id: 17, children: [{ name: '0113', id: 16 }] }] }, { name: '01', id: 21, children: [{ name: '012', id: 20, children: [{ name: '0121', id: 19 }] }] }, { name: '01', id: 24, children: [{ name: '012', id: 23, children: [{ name: '0122', id: 22 }] }] }, { name: '01', id: 27, children: [{ name: '012', id: 26, children: [{ name: '0123', id: 25 }] }] }] }];
const rewrite = (tree) => {
const lookup = objectScan(['**(^children$)'], {
useArraySelector: false,
filterFn: ({ parent, property, value, context }) => {
if (value.name in context) {
if (Array.isArray(value.children)) {
if (!Array.isArray(context[value.name].children)) {
context[value.name].children = [];
}
context[value.name].children.push(...value.children);
}
parent.splice(property, 1);
} else {
context[value.name] = value;
}
}
})(tree, {});
const cmpFn = (a, b) => a.name.localeCompare(b.name);
Object.values(lookup)
.filter((v) => Array.isArray(v.children))
.forEach((v) => v.children.sort());
tree.sort(cmpFn);
};
console.log(rewrite(myTree));
// => undefined
console.log(myTree);
// => [ { id: 0, name: 'ROOT', children: [ { name: '01', id: 27, children: [ { name: '012', id: 26, children: [ { name: '0123', id: 25 }, { name: '0122', id: 22 }, { name: '0121', id: 19 } ] }, { name: '011', id: 17, children: [ { name: '0113', id: 16 }, { name: '0111', id: 7 }, { name: '0112', id: 4 } ] }, { name: '013', id: 14, children: [ { name: '0133', id: 13 }, { name: '0132', id: 10 }, { name: '0131', id: 1 } ] } ] } ] } ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#16.0.0"></script>
Disclaimer: I'm the author of object-scan
I have the following JSON array:
[
{ id: 1, name: "P1", groups: [ { id: 1.1, name: "G1.1" }, { id: 1.2, name:"G1.2" }]},
{ id: 2, name: "P2", groups: [ { id: 2.1, name: "G2.1" }, { id: 2.2, name:"G2.2" }]}
];
What is the most efficient method to convert it to the following structure using Javascript ES6?
[
{ id: 1, name: "P1", group_id: 1.1, group_name: "G1.1"},
{ id: 1, name: "P1", group_id: 1.2, group_name: "G1.2"},
{ id: 2, name: "P2", group_id: 1.1, group_name: "G2.1"},
{ id: 2, name: "P2", group_id: 1.1, group_name: "G2.2"},
]
There are some problems.
If it is a json array, it should be like this.
var obj = [{
"id": 1,
"name": "P1",
"groups": [{
"id": 1.1,
"name": "G1.1"
}, {
"id": 1.2,
"name": "G1.2"
}]
},
{
"id": 2,
"name": "P2",
"groups": [{
"id": 2.1,
"name": "G2.1"
}, {
"id": 2.2,
"name": "G2.2"
}]
}
];
you can validate json arrays using this link
it little bit confused about your out put.That's why it made voting down.
After set an array, you can use ForEach for that.
var obj = [{
"id": 1,
"name": "P1",
"groups": [{
"id": 1.1,
"name": "G1.1"
}, {
"id": 1.2,
"name": "G1.2"
}]
},
{
"id": 2,
"name": "P2",
"groups": [{
"id": 2.1,
"name": "G2.1"
}, {
"id": 2.2,
"name": "G2.2"
}]
}
];
res = [];
obj.forEach((e)=>{
e.groups.forEach((group)=>{
res.push({
"id" : e.id,
"name" : e.name,
"group_id" : group.id,
"group_name" : group.name
});
});
});
console.log(res);
You can achieve this using forEach.
Try the following:
var arr=[
{ id: 1, name: "P1", groups: [ { id: 1.1, name: "G1.1" }, { id: 1.2, name:"G1.2" }]},
{ id: 2, name: "P2", groups: [ { id: 2.1, name: "G2.1" }, { id: 2.2, name:"G2.2" }]}
];
result = [];
arr.forEach((o)=>{
o.groups.forEach((group)=>{
result.push({
"id" : o.id,
"name" : o.name,
"group_id" : group.id,
"group_name" : group.name
});
});
});
console.log(result);
I am looking to find a way to normalize nested many to many relationships.
However, it has been going when the model is a few levels deep, such as Categories in the example below. In the output the location id 27 only contains category ids 1 and 7 (which I assume is because that is what the last version of location 27 shows). What is the best approach in normalizing this type of data?
Would using shop id in the location be a good idea? i.e.
"locations": {
"1-27": {
"id": 27,
"label": "Birmingham",
"categories": [
1,
2,
7
]
},
"2-27": {
"id": 27,
"label": "Birmingham",
"categories": [
1,
7
]
}
},
Thanks for taking the time to read this!
Additional Data Details
Database
Shops
id
label
Locations
id
label
Location_Shop
location_id
shop_id
Categories
id
label
Category_Location
category_id
location_id
Example Data
[
{
id: 1,
label: 'First Shop',
locations: [
{
id: 27,
label: 'Birmingham',
categories: [
{
id: 1,
label: 'Car Park',
},
{
id: 2,
label: 'Petrol Station',
},
{
id: 7,
label: 'Bakery',
},
],
},
],
},
{
id: 2,
label: 'Second Shop',
locations: [
{
id: 27,
label: 'Birmingham',
categories: [
{
id: 1,
label: 'Car Park',
},
{
id: 7,
label: 'Bakery',
},
],
},
],
},
]
Code
const categorySchema = new schema.Entity('categories');
const locationSchema = new schema.Entity('locations', {
categories: [categorySchema],
});
const shopSchema = new schema.Entity('shops', {
locations: [locationSchema],
});
Output
{
"entities": {
"categories": {
"1": {
"id": 1,
"label": "Car Park"
},
"2": {
"id": 2,
"label": "Petrol Station"
},
"7": {
"id": 7,
"label": "Bakery"
}
},
"locations": {
"27": {
"id": 27,
"label": "Birmingham",
"categories": [
1,
7
]
}
},
"shops": {
"1": {
"id": 1,
"label": "First Shop",
"locations": [
27
]
},
"2": {
"id": 2,
"label": "Second Shop",
"locations": [
27
]
}
}
},
"result": [
1,
2
]
}