I am trying to generate tree structure data, the problem is in helper function where I have an empty array children in which I want to push object with data, but get undefined
I do not get undefined when test it separately.
let b = {x: 1}
let a = []
a.push(b)
console.log(a)
function forestMockDataGenerator(n, m) {
const chance = new Chance(Math.random());
const mock = Array.from(Array(n).keys());
return mock.map(function (tree) {
tree = {
datum: chance.name(),
children: [],
};
return helper(tree, 1);
function helper(mock, count) {
if (isCountNotEqualToM(count, m)) {
mock.children.push({
datum: equalOneAddressEqualTwoPhone(count),
children: [],
});
helper(mock.children, (count = count + 1));
}
function equalOneAddressEqualTwoPhone(count) {
return count === 1 ? chance.address() : chance.phone();
}
function isCountNotEqualToM(count, m) {
return count !== m ? true : false;
}
return mock;
}
});
}
example 1
example 2
the data should have the next format
[
{
"name": "String",
"children": [
{
"name": "String",
"children": [
// ...
]
}, {
"name": "String",
"children": [
// ...
]
},
// ...
]
}, {
"name": "String",
"children": [
// ...
]
},
// ...
]
You yould use independent creation of children by handing over the decremented depth.
function forestMockDataGenerator(n, m) {
if (!m) return [];
return Array.from({ length: n }, (_, i) => ({
name: `name${i}`,
children: forestMockDataGenerator(n, m - 1),
}));
}
console.log(forestMockDataGenerator(5, 3));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
I have a Json data that I want to have in a different format.
My original json data is:
{
"info": {
"file1": {
"book1": {
"lines": {
"102:0": [
"102:0"
],
"105:4": [
"106:4"
],
"106:4": [
"107:1",
"108:1"
]
}
}
}
}
}
And I want to map it as following:
{
"name": "main",
"children": [
{
"name": "file1",
"children": [
{
"name": "book1",
"group": "1",
"lines": [
"102",
"102"
],
[
"105",
"106"
],
[
"106",
"107",
"108"
]
}
],
"group": 1,
}
],
"group": 0
}
But the number of books and number of files will be more. Here in the lines the 1st part (before the :) inside the "" is taken ("106:4" becomes "106"). The number from the key goes 1st and then the number(s) from the value goes and make a list (["106", "107", "108"]). The group information is new and it depends on parent-child information. 1st parent is group 0 and so on. The first name ("main") is also user defined.
I tried the following code so far:
function build(data) {
return Object.entries(data).reduce((r, [key, value], idx) => {
//const obj = {}
const obj = {
name: 'main',
children: [],
group: 0,
lines: []
}
if (key !== 'reduced control flow') {
obj.name = key;
obj.children = build(value)
if(!(key.includes(":")))
obj.group = idx + 1;
} else {
if (!obj.lines) obj.lines = [];
Object.entries(value).forEach(([k, v]) => {
obj.lines.push([k, ...v].map(e => e.split(':').shift()))
})
}
r.push(obj)
return r;
}, [])
}
const result = build(data);
console.log(result);
The group information is not generating correctly. I am trying to figure out that how to get the correct group information. I would really appreciate if you can help me to figure it out.
You could use reduce method and create recursive function to build the nested structure.
const data = {"info":{"file1":{"book1":{"lines":{"102:0":["102:0"],"105:4":["106:4"],"106:4":["107:1","108:1"]}}}}}
function build(data) {
return Object.entries(data).reduce((r, [key, value]) => {
const obj = {}
if (key !== 'lines') {
obj.name = key;
obj.children = build(value)
} else {
if (!obj.lines) obj.lines = [];
Object.entries(value).forEach(([k, v]) => {
obj.lines.push([k, ...v].map(e => e.split(':').shift()))
})
}
r.push(obj)
return r;
}, [])
}
const result = build(data);
console.log(result);
I couldn't understand the logic behind group property, so you might need to add more info for that, but for the rest, you can try these 2 functions that recursively transform the object into what you are trying to get.
var a = {"info":{"file1":{"book1":{"lines":{"102:0":["102:0"],"105:4":["106:4"],"106:4":["107:1","108:1"]}}}}};
var transform = function (o) {
return Object.keys(o)
.map((k) => {
return {"name": k, "children": (k === "lines" ? parseLines(o[k]) : transform(o[k])) }
}
)
}
var parseLines = function (lines) {
return Object.keys(lines)
.map(v => [v.split(':')[0], ...(lines[v].map(l => l.split(":")[0]))])
}
console.log(JSON.stringify(transform(a)[0], null, 2));
my backend service send me list of node as an array. but I need is, each next node is value of its previous node(SEE EXAMPLE). I want whole list as nested object in singe object.
WHAT I HAVE:
[
{
"nodeId": 1,
},
{
"nodeId": 3,
},
{
"nodeId": 16,
}
]
WHAT I NEED:
[
{
"nodeId": 1,
"staticChild": [
{
"nodeId": 3,
"staticChild": [
{
"nodeId": 16,
}
]
}
]
}
]
You could reduce the array from the right side and build a new object with a staticChild property.
var array = [{ nodeId: 1 }, { nodeId: 3 }, { nodeId: 16 }],
result = array.reduceRight((a, b) => ({ ...b, staticChild: [a] }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Based on the input / output you provided, you can use a recursive funtion like :
const data = [{
nodeId: 1
},
{
nodeId: 3
},
{
nodeId: 16
}
];
const transform = data => {
const [node, ...rest] = data;
if (rest.length > 0) {
return {
...node,
staticChild: [transform(rest)]
};
} else {
return {
...node,
hasChildren: false
};
}
};
const result = transform(data);
console.log(result);
At first reverse the array and the make an iteration over the revered array using reduce() to make your desire format.
let data = [{"nodeId": 1},{"nodeId": 3},{"nodeId": 16}]
data = data.reverse().reduce((old, cur) => {
if (!old.length) {
old = [cur]
} else {
cur['staticChild'] = old
old = [cur]
}
return old
}, [])
console.log(data)
You can use the reduceRight() array method to perform the transformation.
const data = [{
"nodeId": 1,
},
{
"nodeId": 3,
},
{
"nodeId": 16,
}
]
const nested = data.reduceRight((acc, item) => {
return [ { ...item, staticChild: acc } ]
}, []);
console.log(nested);
Or more succinctly:
const nested = data.reduceRight((acc, item) => [ { ...item, staticChild: acc } ],[]);
I try to write a function in JavaScript which filter an array by a selected property (an value).
But it works for 2 level only I do not understand what do I missing.
The data I want to filter:
var data = [
{
name: "john_pc",
children: [
{
name: "sabrina_pc",
children: [
{
name: "sabrina_pc"
},
{
name: "john_pc"
}
]
},
{
name: "john_pc"
}
]
},
{
name: "sabrina_pc"
}
]
The childrenFilter funciton :
const childrenFilter = (childrenData, filters) => {
let filteredData = childrenData.filter(item => {
for (var property in filters) {
var optionalValues = filters[property];
var value = item[property];
if (item.children) {
item.children = childrenFilter(item.children, filters);
}
let hasValue = value == optionalValues;
if (hasValue) {
return true;
}
return false;
}
return false;
}, this);
return filteredData;
}
Calling the function:
As you can see the 'childrenFilter' get an object which the key is property in the data and the key is value I want to keep.
let result = childrenFilter(data, {
"name": "a1"
});
console.log(JSON.stringify(result, null, 2))
The wanted result :
[
{
"name": "john_pc",
"children": [
{
"name": "sabrina_pc",
"children": [
{
"name": "john_pc"
}
]
},
{
"name": "john_pc"
}
]
}
]
Your filter function does not take into account whether or not children elements match the pattern, therefore even though some child elements of the object match the pattern, the object itself is being filtered out.
Here is the explanation:
{
name: "a2", // does not match filter {name:'a1} so is removed alongside child objects
children: [ // gets removed with parent object
{
name: "a2"
},
{
name: "a1"
}
]
}
This should produce the desired output:
const childrenFilter = (childrenData, filters) => {
let filteredData = childrenData.filter(item => {
for (var property in filters) {
var optionalValues = filters[property];
var value = item[property];
if (item.children) {
item.children = childrenFilter(item.children, filters);
}
let hasValue = value == optionalValues;
if (hasValue || item.children.length) { // include item when children mathes the pattern
return true;
}
return false;
}
return false;
}, this);
return filteredData;
}
You could build new array for each step of filtering, beginning from the leaves and check if this contains the wanted value.
This approach generates new objects and does not mutate the original data.
function filter(array, filters) {
return array.reduce((r, o) => {
var children = filter(o.children || [], filters);
return children || Object.entries(filters).every(([k, v]) => o[k] === v)
? (r || []).concat(Object.assign({}, o, children && { children }))
: r;
}, undefined);
}
var data = [{ name: "a1", children: [{ name: "a2", children: [{ name: "a2" }, { name: "a1" }] }, { name: "a1" }] }, { name: "b1" }];
console.log(filter(data, { name: "a1" }));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Array 1 = Accessories:Bracket,Accessories:Clamp,Actuator:Accessories,Actuator:Accessories:Bracket,Actuator:Accessories:Clamp,Actuator:Clevis
Array 2 = 24092859,24092784,24094450,24094451,24110219,24092811
Required Output =
[
{
"text": "Accessories",
"children": [
{
"text": "Bracket",
"children": [],
"mtdtId": "24092859"
},
{
"text": "Clamp",
"children": [],
"mtdtId": "24092784"
}
],
"mtdtId": "24092859,24092784"
},
{
"text": "Actuator",
"children": [
{
"text": "Accessories",
"children": [
{
"text": "Bracket",
"children": [],
"mtdtId": "24094451"
},
{
"text": "Clamp",
"children": [],
"mtdtId": "24110219"
}
],
"mtdtId": "24110219,24094451"
},
{
"text": "Clevis",
"children": [],
"mtdtId": ""
}
],
"mtdtId": "24110219,24094451"
}
]
the parent should contain the id's of the child nodes.
const array1 = "Accessories:Bracket,Accessories:Clamp,Actuator:Accessories,Actuator:Accessories:Bracket,Actuator:Accessories:Clamp,Actuator:Clevis".split(
","
);
const array2 = "24092859,24092784,24094450,24094451,24110219,24092811".split(
","
);
const output = array1.reduce(
(topLevelNodes, path, i) => {
let nodes = topLevelNodes;
let mtdId = array2[i];
path.split(":").forEach(text => {
let node = nodes.filter(child => child.text === text)[0];
if (node) {
node.mtdId += "," + mtdId;
} else {
nodes.push((node = { text, children: [], mtdId }));
}
nodes = node.children;
});
return topLevelNodes;
},
[]
);
console.log(output);
You could use a hash table for the nested elements and get later all values of mtdtId for grouping of the children elements. I suggest to use a different property for the collection.
var array1 = 'Engines:Combustion,Engines:Combustion:AeroThermal,Engines:Combustion:Fuel Systems,Engines:Combustion:Mechanical,Engines:Fans & Compressors,Engines:Fans & Compressors:Centrifugal Compressor Aero'.split(','),
array2 = '001,002,003,004,005,006'.split(','),
result = [];
array1.forEach(function (a, i) {
a.split(':').reduce(function (r, k, j, kk) {
if (!r[k]) {
r[k] = { _: [] };
r._.push(j + 1 === kk.length ? { text: k, mtdtId: array2[i], children: r[k]._ } : { text: k, children: r[k]._ });
}
return r[k];
}, this);
}, { _: result });
result.reduce(function iter(r, a) {
var temp = a.mtdtId ? [a.mtdtId] : [];
if (Array.isArray(a.children)) {
temp = a.children.reduce(iter, temp);
}
if (a.mtdtId !== temp.join()) {
a.collected = temp.join();
}
return r.concat(temp);
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
There is an array:
let docs = [
{ "_id":"1", parent:"_", "title":"one"},
{ "_id":"2", parent:"1", "title":"two"},
{ "_id":"4", parent:"_", "title":"title"},
{ "_id":"5", parent:"4", "title":"www"},
{"_id":"_", "name":"root" },
];
I need to get out of it that's a tree:
{'_id':'_','name':'root','child':
[
{'_id':'1','parent':'_','title':'one','child':
[
{'_id':'2','parent':'1','title':'two','child':[]}
]
},
{'_id':'4','parent':'_','title':'title','child':
[
{'_id':'6','parent':'4','title':'vvv','child':[]}
]
}
]
}
But my code only works if the parent element is always higher on the list than the children, and I want to make that work universally.
This is code:
let node = {};
for (let doc of docs) {
doc.child = [];
node[doc._id] = doc;
if (typeof doc.parent === "undefined")
tree = doc;
else
node[doc.parent].child.push(doc);
}
console.log('tree->', JSON.stringify(tree));
code on codepen:
http://codepen.io/alex183/pen/OWvrPG?editors=0112
You can create recursive function using reduce method and basically check in each iteration of the parent property of current object is equal to passed parent param in function call.
let docs = [
{ "_id":"1", parent:"_", "title":"one"},
{ "_id":"2", parent:"1", "title":"two"},
{ "_id":"4", parent:"_", "title":"title"},
{ "_id":"5", parent:"4", "title":"www"},
{"_id":"_", "name":"root" }
];
function makeTree(data, parent = undefined) {
return data.reduce((r, e) => {
// check if current e.parent is equal to parent
if (e.parent === parent) {
// make a copy of current e so we keep original as is
const o = { ...e }
// set value as output of recursive call to child prop
o.child = makeTree(data, e._id)
// push to accumulator
r.push(o)
}
return r
}, [])
}
console.log(makeTree(docs))
This is a proposal with Array#reduce and Map. It sorts the array in advance.
var docs = [{ _id: "1", parent: "_", title: "one" }, { _id: "2", parent: "1", title: "two" }, { _id: "4", parent: "_", title: "title" }, { _id: "5", parent: "4", title: "www" }, { _id: "_", name: "root" }],
order = { undefined: -2, _: -1 },
tree = docs
.sort((a, b) => (order[a.parent] || a.parent) - (order[b.parent] || b.parent) || a._id - b._id)
.reduce(
(m, a) => (
m
.get(a.parent)
.push(Object.assign({}, a, { child: m.set(a._id, []).get(a._id) })),
m
),
new Map([[undefined, []]])
)
.get(undefined);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The quick and dirty way is to use a sort function.
docs = docs.sort((a, b) => (a._id - b._id));