Converting Array of dot notation strings to nested Json-Object - javascript

I have the following problem. I'd like to convert an array of strings in dot notation to a nested json object.
example input: obj = {"project1.foo.a" : "value", "project1.foo.b" : "value","project1.bar" : "value", "project2.foo.a" : "value", "project2.foo.b" : "value", "project2.foo.c" : "value", "project2.foo.bar" : "value"};
desired output:
{
"id": 0,
"title": "Projects",
"has_children": 1,
"level": 1,
"value":"string",
"children": [
{
"id": 1,
"title": "Project 1",
"has_children": true,
"level": 2,
"value":"string",
"children": [
{
"id": 11,
"title": "foo",
"has_children": true,
"level": 3,
"value":"string",
"children": [
{
"id": 111,
"title": "a",
"has_children": false,
"level": 4,
"value":"string",
"children": [
]
},
{
"id": 112,
"title": "b",
"has_children": false,
"level": 4,
"value":"string",
"children": [
]
}
]
},
{
"id": 12,
"title": "bar",
"has_children": false,
"level": 3,
"value":"string",
"children": [
]
}
]
},
{
"id": 2,
"title": "Project 2",
"has_children": true,
"level": 2,
"value":"string",
"children": [
{
"id": 21,
"title": "foo",
"has_children": true,
"level": 3,
"value":"string",
"children": [
{
"id": 211,
"title": "a",
"has_children": false,
"level": 4,
"value":"string",
"children": [
]
},
{
"id": 212,
"title": "b",
"has_children": false,
"level": 4,
"value":"string",
"children": [
]
},
{
"id": 213,
"title": "c",
"has_children": false,
"level": 4,
"value":"string",
"children": [
]
}
]
},
{
"id": 22,
"title": "bar",
"has_children": false,
"level": 3,
"value":"string",
"children": [
]
}
]
}
]
}
There are great functions to unflatten the example object, e. g. with lodash, but I'm not able to add the descendants recursively into the childrens array.
example code:
const unflatten = (flattedObject) => {
let result = {};
_.keys(flattedObject).forEach(function (key, value){
_.set(result, key, flattedObject[key]);
})
return result;
}
console.log(unflatten(obj));
Maybe someone knows a good/neat or at least working approach to do this. :D I want to feed this plugin with the data https://travistidwell.com/jquery.treeselect.js/.
Related posts: How to populate jquery treeselect widget?
How to unflatten a JavaScript object in a daisy-chain/dot notation into an object with nested objects and arrays?

Related

Need to add parentId to child in multi-dimensional Array of JSON data

I have a multidimensional array of JSON data with 'n' number of nested children. My task is to add UniqueId to parent JSON and that uniqueId should be added as parentId to the child. Can you please help in javascript. Thanks
Note:
The number of child its recursive, and there can be any number of children. For this purpose, we can have a deep level of three
Input :
[{
"text": 1527978678434,
"value": 1527978678434,
"children": [{
"text": 1292232152442,
"value": 1292232152442,
"children": [{
"text": 474194771845,
"value": 474194771845,
"children": []
},
{
"text": 468086178830,
"value": 468086178830,
"children": []
}
]
},
{
"text": 1067869237589,
"value": 1067869237589,
"children": []
},
{
"text": 1166591731429,
"value": 1166591731429,
"children": []
},
]
}]
The Required Output:
[{
"text": 1527978678434,
"value": 1527978678434,
"parentId": 0,
"uniqueId": 1,
"children": [{
"text": 1292232152442,
"value": 1292232152442,
"parentId": 1,
"uniqueId": 2,
"children": [{
"text": 474194771845,
"value": 474194771845,
"parentId": 2,
"uniqueId": 3,
"children": []
},
{
"text": 468086178830,
"value": 468086178830,
"parentId": 2,
"uniqueId": 4,
"children": []
}
]
},
{
"text": 1067869237589,
"value": 1067869237589,
"parentId": 1,
"uniqueId": 5,
"children": []
},
{
"text": 1166591731429,
"value": 1166591731429,
"parentId": 1,
"uniqueId": 6,
"children": []
},
{
"text": 111221786011,
"value": 111221786011,
"parentId": 1,
"uniqueId": 7,
"children": []
},
{
"text": 641372005975,
"value": 641372005975,
"parentId": 1,
"uniqueId": 8,
"children": [{
"text": 23082640100,
"value": 23082640100,
"parentId": 8,
"uniqueId": 9,
"children": []
}]
}
]
}]
Consider data is your array.
Just do the DFS and alter the data on the fly. + Having a global to hold the increased ID.
let lastUniqueId = 0;
function addIds(children, parentId) {
(children || []).forEach(r => {
r.parentId = parentId;
r.uniqueId = ++lastUniqueId;
addIds(r.children, r.uniqueId);
});
}
addIds(data, lastUniqueId);
This seems something you can do using a recursive function while keeping track of a shared uniqueId counter, assuming the nesting doesn't go incredibly deep:
const data = [{
"text": 1527978678434,
"value": 1527978678434,
"children": [{
"text": 1292232152442,
"value": 1292232152442,
"children": [{
"text": 474194771845,
"value": 474194771845,
"children": []
},
{
"text": 468086178830,
"value": 468086178830,
"children": []
}
]
},
{
"text": 1067869237589,
"value": 1067869237589,
"children": []
},
{
"text": 1166591731429,
"value": 1166591731429,
"children": []
},
]
}];
function convert(array) {
let uniqueId = 0;
function mapData(data, parentId) {
const myId = ++uniqueId;
const result = {
...data, // copy all data
uniqueId, parentId, // add our new fields
// and handle the children recursively
children: data.children.map(c => mapData(c, myId)),
};
// Don't mind this, just tricking JS in displaying the children array at the bottom during console.log
const children = result.children; delete result.children; result.children = children;
return result;
}
return array.map(d => mapData(d, 0));
}
const result = convert(data);
console.log(result);

Transformation of nested object

I am new to JavaScript and Node JS
want to transform the following nested object with student
Data:
[{
"id": 1,
"name": "A",
"children": [{
"id": 2,
"name": "B",
"children": [{
"id": 3,
"name": "C"
},
{
"id": 4,
"name": "D"
}
]
}]
}]
to
Expected:
[{
"student": {
"id": 1,
"name": "A"
},
"children": [{
"student": {
"id": 2,
"name": "B"
},
"children": [{
"student": {
"id": 3,
"name": "C"
}
},
{
"student": {
"id": 4,
"name": "D"
}
}
]
}]
}]
I guess you are seeking a solution for an array with multiple student objects. So you can use the map method to modify them.
const original = [{
"id": 1,
"name": "A",
"children": [{
"id": 2,
"name": "B",
"children": [{
"id": 3,
"name": "C"
},
{
"id": 4,
"name": "D"
}
]
}]
}]
const modified = original.map(stu => {
return {
student: {
id: stu.id,
name: stu.name,
},
children: stu.children
}
})

Filter Array of user objects by array of roles in Javascript

I have an array of user objects.
I want to filter them based on the array of user roles.
filter = ['ROLE_SELLER', 'ROLE_BANK', 'ROLE_CPF', 'ROLE_SLA', 'ROLE_LDAU']
const users = [{
"id": 1,
"email": "user1#test.com",
"name": "User1",
"roles": [{
"id": 1,
"code": "ROLE_ADMINISTRATOR",
"name": "Administrator"
},
{
"id": 2,
"code": "ROLE_SELLER",
"name": "Seller"
}
]
}, {
"id": 2,
"email": "user2#test.com",
"name": "User2",
"roles": [{
"id": 1,
"code": "ROLE_ADMINISTRATOR",
"name": "Administrator"
}]
}, {
"id": 3,
"email": "user3#test.com",
"name": "User3",
"roles": [{
"id": 1,
"code": "ROLE_ADMINISTRATOR",
"name": "Administrator"
}]
}, {
"id": 4,
"email": "user4#test.com",
"name": "User4",
"roles": [{
"id": 1,
"code": "ROLE_ADMINISTRATOR",
"name": "Administrator"
},
{
"id": 2,
"code": "ROLE_SELLER",
"name": "Seller"
}
]
}, {
"id": 5,
"email": "user5#test.com",
"name": "User5",
"roles": [{
"id": 5,
"code": "ROLE_LAWYER",
"name": "Lawyer"
}]
}, {
"id": 6,
"email": "user6#test.com",
"name": "User6",
"roles": [{
"id": 2,
"code": "ROLE_SELLER",
"name": "Seller"
}]
},
{
"id": 7,
"email": "user7#test.com",
"name": "User7",
"roles": [{
"id": 9,
"code": "ROLE_SLA",
"name": "sla"
}]
},
{
"id": 8,
"email": "user8#test.com",
"name": "User8",
"roles": [{
"id": 8,
"code": "ROLE_BANK",
"name": "Bank"
}]
},
{
"id": 9,
"email": "user9#test.com",
"name": "User9",
"roles": [{
"id": 7,
"code": "ROLE_CPF",
"name": "CPF"
}]
}
]
const filter = ['ROLE_SELLER', 'ROLE_BANK', 'ROLE_CPF', 'ROLE_SLA', 'ROLE_LDAU']
const filteredUsers = users.filter(user => !user.roles.find(role => filter.includes(role.id)))
console.log(filteredUsers)
Expected result
[{
"id": 1,
"email": "user1#test.com",
"name": "User1",
"roles": [{
"id": 1,
"code": "ROLE_ADMINISTRATOR",
"name": "Administrator"
},
{
"id": 2,
"code": "ROLE_SELLER",
"name": "Seller"
}
]
}, {
"id": 4,
"email": "user4#test.com",
"name": "User4",
"roles": [{
"id": 1,
"code": "ROLE_ADMINISTRATOR",
"name": "Administrator"
},
{
"id": 2,
"code": "ROLE_SELLER",
"name": "Seller"
}
]
}, {
"id": 6,
"email": "user6#test.com",
"name": "User6",
"roles": [{
"id": 2,
"code": "ROLE_SELLER",
"name": "Seller"
}]
},
{
"id": 7,
"email": "user7#test.com",
"name": "User7",
"roles": [{
"id": 9,
"code": "ROLE_SLA",
"name": "sla"
}]
},
{
"id": 8,
"email": "user8#test.com",
"name": "User8",
"roles": [{
"id": 8,
"code": "ROLE_BANK",
"name": "Bank"
}]
},
{
"id": 9,
"email": "user9#test.com",
"name": "User9",
"roles": [{
"id": 7,
"code": "ROLE_CPF",
"name": "CPF"
}]
}
]
I am trying to image what result you want to receive....
You want to filter of users by array with possible roles, another worlds if user has one of roles of filter array you want to pass his to 'filteredUsers' array?
filter.includes(role.id) - I guess it is wrong, may be you want to
filter by role.code?
Array.find() doesn't support Explorer
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
Array.includes() - doesn't support Explorer too.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
I think the reason why that was happening was that you are trying to match the role id with the 'filter' array which contains the role code. From what i observed from the expected answer, you are looking for user who has any one of their role fits the filter. So do the following will filter by role code and with the expected result
const filteredUsers = users.filter((user) => {
return user.roles.map(role=>filter.includes(role.code)).includes(true)
})
this line of code basically map the filter to every role object to every user, if their role code is included in the filter it will add a true to the array(return array of map()) and for the filter function if the map() return array contains true then true(so basically a || for the whole array)

How do I flatten a (forest of) trees?

I have a forest of trees of arbitrary height, more or less like this:
let data = [
{ "id": 2, "name": "AAA", "parent_id": null, "short_name": "A" },
{
"id": 10, "name": "BBB", "parent_id": null, "short_name": "B", "children": [
{
"id": 3, "name": "CCC", "parent_id": 10, "short_name": "C", "children": [
{ "id": 6, "name": "DDD", "parent_id": 3, "short_name": "D" },
{ "id": 5, "name": "EEE", "parent_id": 3, "short_name": "E" }
]
},
{
"id": 4, "name": "FFF", "parent_id": 10, "short_name": "F", "children": [
{ "id": 7, "name": "GGG", "parent_id": 4, "short_name": "G" },
{ "id": 8, "name": "HHH", "parent_id": 4, "short_name": "H" }
]
}]
}
];
And I'm trying to produce a representation of all the root-to-leaves paths, something like this
[
[
{
"id": 2,
"name": "AAA"
}
],
[
{
"id": 10,
"name": "B"
},
{
"id": 3,
"name": "C"
},
{
"id": 6,
"name": "DDD"
}
],
[
{
"id": 10,
"name": "B"
},
{
"id": 3,
"name": "C"
},
{
"id": 5,
"name": "EEE"
}
],
[
{
"id": 10,
"name": "B"
},
{
"id": 4,
"name": "F"
},
{
"id": 7,
"name": "GGG"
}
],
[
{
"id": 10,
"name": "B"
},
{
"id": 4,
"name": "F"
},
{
"id": 8,
"name": "HHH"
}
]
]
So I wrote the following code:
function flattenTree(node, path = []) {
if (node.children) {
return node.children.map(child => flattenTree(child, [...path, child]));
} else {
let prefix = path.slice(0, path.length - 1).map(n => ({ id: n.id, name: n.short_name }));
let last = path[path.length - 1];
return [...prefix, { id: last.id, name: last.name } ];
}
}
let paths = data.map(n => flattenTree(n, [n]));
but paths comes out with extra nesting, like this:
[
[
{
"id": 2,
"name": "AAA"
}
],
[
[
[
{
"id": 10,
"name": "B"
},
{
"id": 3,
"name": "C"
},
{
"id": 6,
"name": "DDD"
}
],
[
{
"id": 10,
"name": "B"
},
{
"id": 3,
"name": "C"
},
{
"id": 5,
"name": "EEE"
}
]
],
[
[
{
"id": 10,
"name": "B"
},
{
"id": 4,
"name": "F"
},
{
"id": 7,
"name": "GGG"
}
],
[
{
"id": 10,
"name": "B"
},
{
"id": 4,
"name": "F"
},
{
"id": 8,
"name": "HHH"
}
]
]
]
]
I lost count of the many ways in which I tried to fix this, but it does look like the algorithm should not produce the extra nesting -- or my eyes are just so crossed by now that I couldn't see my mistake if someone stuck their finger on it.
Can someone help? Feel free to peruse this JSFiddle https://jsfiddle.net/png7x9bh/66/
The extra nestings are created by map. map just wraps the results into an array and returns them, it doesn't care if it is called on child nodes or not. Use reduce and just concat (or push, whatever suits your performance) the results into the first level array directly:
let data = [{"id":2,"name":"AAA","parent_id":null,"short_name":"A"},{"id":10,"name":"BBB","parent_id":null,"short_name":"B","children":[{"id":3,"name":"CCC","parent_id":10,"short_name":"C","children":[{"id":6,"name":"DDD","parent_id":3,"short_name":"D"},{"id":5,"name":"EEE","parent_id":3,"short_name":"E"}]},{"id":4,"name":"FFF","parent_id":10,"short_name":"F","children":[{"id":7,"name":"GGG","parent_id":4,"short_name":"G"},{"id":8,"name":"HHH","parent_id":4,"short_name":"H"}]}]}];
function flattenTree(node, path = []) {
let pathCopy = Array.from(path);
pathCopy.push({id: node.id, name: node.name});
if(node.children) {
return node.children.reduce((acc, child) => acc.concat(flattenTree(child, pathCopy)), []);
}
return [pathCopy];
}
let result = data.reduce((result, node) => result.concat(flattenTree(node)), []);
console.log(JSON.stringify(result, null, 3));

Convert tree structure to hierarchical array

I have puzzling with this problem for hours. I am using Angular and angular-ui-tree to create an editable tree with unknown number of leaves.
I need to flatten and convert the tree into a hierarchical array in order to pass it to MongoDb. I am also using Underscore.js as a utility
var tree = [{
"_id": 1,
"title": "node1",
"nodes": [{
"_id": 11,
"title": "node1.1",
"nodes": [{
"_id": 111,
"title": "node1.1.1",
"children": [{
"_id": 1111,
"title": "node1.1.1.1",
"children": []
}]
}]
}, {
"_id": 12,
"title": "node1.2",
"children": []
}]
}, {
"_id": 2,
"title": "node2",
"children": [{
"id": 21,
"title": "node2.1",
"children": []
}, {
"_id": 22,
"title": "node2.2",
"children": []
}]
}, {
"_id": 3,
"title": "node3",
"children": [{
"id": 31,
"title": "node3.1",
"children": []
}]
}, {
"_id": 4,
"title": "node4",
"children": [{
"_id": 41,
"title": "node4.1",
"children": []
}]
}]
//desired output
//parentId is null for top leaf nodes, path is String made by the parents' ids
[
{ "_id": 1, "title": "node1", "parentId": null, "path" : ""},
{ "_id": 11, "title": "node1.1", "parentId": 1, "path" : ",1"},
{ "_id": 111, "title": "node1.1.1", "parentId": 11, "path" : ",1,11"},
{ "_id": 1111, "title": "node1.1.1.1", "parentId": 111, "path" : ",1,11,111"},
{ "_id": 12, "title": "node1.1", "parentId": 1, "path" : ",1"},
{ "_id": 2, "title": "node2", "parentId": null, "path" : ""},
{ "_id": 21, "title": "node2.1", "parentId": 2, "path" : ",2"},
{ "_id": 3, "title": "node3", "parentId": null, "path" : ""},
{ "_id": 31, "title": "node3.1", "parentId": 3, "path" : ",3"},
{ "_id": 4, "title": "node4", "parentId": null, "path" : ""},
{ "_id": 41, "title": "node4.1", "parentId": 4, "path" : ",4"},
]
I'd use recursion here to "walk" down the tree. Note that your input tree sometimes uses "children" and sometimes uses "nodes" to denote its array of children; I have changed it to "children" throughout.
var wholeTree = [{
"_id": 1,
"title": "node1",
"children": [{
"_id": 11,
"title": "node1.1",
"children": [{
"_id": 111,
"title": "node1.1.1",
"children": [{
"_id": 1111,
"title": "node1.1.1.1",
"children": []
}]
}]
}, {
"_id": 12,
"title": "node1.2",
"children": []
}]
}, {
"_id": 2,
"title": "node2",
"children": [{
"id": 21,
"title": "node2.1",
"children": []
}, {
"_id": 22,
"title": "node2.2",
"children": []
}]
}, {
"_id": 3,
"title": "node3",
"children": [{
"id": 31,
"title": "node3.1",
"children": []
}]
}, {
"_id": 4,
"title": "node4",
"children": [{
"_id": 41,
"title": "node4.1",
"children": []
}]
}];
var flattened = flattenTreeToNodes( wholeTree, null, "" );
$("#output").text( JSON.stringify(flattened) );
function flattenTreeToNodes( tree, parentId, basePath ) {
console.log( parentId, basePath );
function createFlattenedNode( treeNode ) {
var path = parentId?basePath + "," + parentId:"";
return {
"_id": treeNode._id,
title: treeNode.title,
parentId: parentId,
path: path
}
}
var nodes = [];
for(var i=0; i<tree.length; i++) {
var treeNode = tree[i];
var flattenedNode = createFlattenedNode(treeNode);
nodes.push ( flattenedNode );
var flattenedChildren = flattenTreeToNodes( treeNode.children, treeNode._id, flattenedNode.path );
nodes = nodes.concat( flattenedChildren );
}
return nodes;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<pre id='output'>
</pre>
A Simple recursive function will do the job :
var out = [
];
var generate = function (collection, parent) {
for (var i = 0; i < collection.length; i++) {
var obj = {
'_id': collection[i]['_id'],
'title': collection[i]['title'],
'parentId': parent ? parent['_id'] : null,
'path': parent ? ',' + parent['_id'] : ''
};
out.push(obj);
if (collection[i].nodes) {
generate(collection[i].nodes, collection[i]);
} else {
generate(collection[i].children, collection[i]);
}
}
};
See example below:
var tree = [
{
'_id': 1,
'title': 'node1',
'nodes': [
{
'_id': 11,
'title': 'node1.1',
'nodes': [
{
'_id': 111,
'title': 'node1.1.1',
'children': [
{
'_id': 1111,
'title': 'node1.1.1.1',
'children': [
]
}
]
}
]
},
{
'_id': 12,
'title': 'node1.2',
'children': [
]
}
]
},
{
'_id': 2,
'title': 'node2',
'children': [
{
'id': 21,
'title': 'node2.1',
'children': [
]
},
{
'_id': 22,
'title': 'node2.2',
'children': [
]
}
]
},
{
'_id': 3,
'title': 'node3',
'children': [
{
'id': 31,
'title': 'node3.1',
'children': [
]
}
]
},
{
'_id': 4,
'title': 'node4',
'children': [
{
'_id': 41,
'title': 'node4.1',
'children': [
]
}
]
}
];
var out = [
];
var generate = function (collection, parent) {
for (var i = 0; i < collection.length; i++) {
var obj = {
'_id': collection[i]['_id'],
'title': collection[i]['title'],
'parentId': parent ? parent['_id'] : null,
'path': parent ? ',' + parent['_id'] : ''
};
out.push(obj);
if (collection[i].nodes) {
generate(collection[i].nodes, collection[i]);
} else {
generate(collection[i].children, collection[i]);
}
}
};
generate(tree, null);
console.log(JSON.stringify(out,null,4));
//prints
[
{
"_id": 1,
"title": "node1",
"parentId": null,
"path": ""
},
{
"_id": 11,
"title": "node1.1",
"parentId": 1,
"path": ",1"
},
{
"_id": 111,
"title": "node1.1.1",
"parentId": 11,
"path": ",11"
},
{
"_id": 1111,
"title": "node1.1.1.1",
"parentId": 111,
"path": ",111"
},
{
"_id": 12,
"title": "node1.2",
"parentId": 1,
"path": ",1"
},
{
"_id": 2,
"title": "node2",
"parentId": null,
"path": ""
},
{
"title": "node2.1",
"parentId": 2,
"path": ",2"
},
{
"_id": 22,
"title": "node2.2",
"parentId": 2,
"path": ",2"
},
{
"_id": 3,
"title": "node3",
"parentId": null,
"path": ""
},
{
"title": "node3.1",
"parentId": 3,
"path": ",3"
},
{
"_id": 4,
"title": "node4",
"parentId": null,
"path": ""
},
{
"_id": 41,
"title": "node4.1",
"parentId": 4,
"path": ",4"
}
]

Categories

Resources