Any nice way to groupBy while transforming values with lodash - javascript

I am inexperienced with lodash, but I believe it can help me transform data into a desired format. I have tried varying level of methods described in the documentation, but I can not wrap my head around everything. I've looked here on SO, a few blogs, and the documentation. I have tried combining groupby and map, but I was unable to resolve the issue. I was also unsure how to log the steps.
Given an array of objects like this:
[
{
"parent": "app",
"value": "id"
"label": "1",
},
{
"parent": "app",
"value": "title"
"label": "Page",
},
{
"parent": "app",
"value": "description"
"label": "Desc page",
},
{
"parent": "home",
"value": "id"
"label": "2",
},
{
"parent": "home",
"value": "title"
"label": "Home",
},
{
"parent": "home",
"value": "description"
"label": "Desc home",
}
]
I would like to convert it to this:
{
"app": {
"id": "1",
"title": "Page",
"description": "Desc page"
},
"home": {
"id": "2",
"title": "Home",
"description": "Desc home"
}
}
Can anyone point me in the right direction?

You don't need lodash to accomplish such behavior. You can easily do this with a reduce.
const values = [
{
"parent": "app",
"value": "id",
"label": "1",
},
{
"parent": "app",
"value": "title",
"label": "Page",
},
{
"parent": "app",
"value": "description",
"label": "Desc page",
},
{
"parent": "home",
"value": "id",
"label": "2",
},
{
"parent": "home",
"value": "title",
"label": "Home",
},
{
"parent": "home",
"value": "description",
"label": "Desc home",
}
];
const mappedValues = values.reduce((acc, val) => {
acc[val.parent] = acc[val.parent] || {};
acc[val.parent][val.value] = val.label;
return acc;
}, {});
console.log(mappedValues);

Related

Extract the parent node name from Tree who has childrens

I want to iterate the tree and need to get the id of all the nodes which has the children in string array. while looping it is just returning me the record but doesn't extract the name of the node.
e.g const result = ['root', 'USER', 'ROLE', 'DASHBOARD', 'BRAND', 'COMPANY'];
{
"id": "root",
"name": "Roles and Permissions",
"children": [
{
"id": "USER",
"name": "USER",
"children": [
{
"id": "1",
"name": "VIEW"
},
{
"id": "2",
"name": "CREATE"
},
{
"id": "3",
"name": "EDIT"
}
]
},
{
"id": "ROLE",
"name": "ROLE",
"children": [
{
"id": "8",
"name": "VIEW"
},
{
"id": "9",
"name": "CREATE"
},
{
"id": "10",
"name": "EDIT"
},
{
"id": "11",
"name": "DELETE"
}
]
},
{
"id": "DASHBOARD",
"name": "DASHBOARD",
"children": [
{
"id": "BRAND",
"name": "BRAND",
"children": [
{
"id": "52",
"name": "VIEW"
},
{
"id": "53",
"name": "CREATE"
},
{
"id": "54",
"name": "EDIT"
},
{
"id": "55",
"name": "DELETE"
}
]
},
{
"id": "COMPANY",
"name": "COMPANY",
"children": [
{
"id": "56",
"name": "VIEW"
},
{
"id": "57",
"name": "CREATE"
},
{
"id": "58",
"name": "EDIT"
},
{
"id": "59",
"name": "DELETE"
}
]
}
]
}
]
}
I tried various looping method to get the list, e.g. but not returning the exact name of the node.
function getParent(nodes) {
if(Array.isArray(nodes.children)) {
return nodes.children.map((node) => getParent(node));
}
return nodes.name;
}
You can store the resp in an array and return that array.
const q = {
"id": "root",
"name": "Roles and Permissions",
"children": [
{
"id": "USER",
"name": "USER",
"children": [
{
"id": "1",
"name": "VIEW"
},
{
"id": "2",
"name": "CREATE"
},
{
"id": "3",
"name": "EDIT"
}
]
},
{
"id": "ROLE",
"name": "ROLE",
"children": [
{
"id": "8",
"name": "VIEW"
},
{
"id": "9",
"name": "CREATE"
},
{
"id": "10",
"name": "EDIT"
},
{
"id": "11",
"name": "DELETE"
}
]
},
{
"id": "DASHBOARD",
"name": "DASHBOARD",
"children": [
{
"id": "BRAND",
"name": "BRAND",
"children": [
{
"id": "52",
"name": "VIEW"
},
{
"id": "53",
"name": "CREATE"
},
{
"id": "54",
"name": "EDIT"
},
{
"id": "55",
"name": "DELETE"
}
]
},
{
"id": "COMPANY",
"name": "COMPANY",
"children": [
{
"id": "56",
"name": "VIEW"
},
{
"id": "57",
"name": "CREATE"
},
{
"id": "58",
"name": "EDIT"
},
{
"id": "59",
"name": "DELETE"
}
]
}
]
}
]
}
let result = []
function r(nodes){
if(Array.isArray(nodes.children)){
result.push(nodes.name);
nodes.children.map((c) => r(c))
return result;
}
return result;
}
console.log(r(q))
You can simply use a recursive function. Here ids is an array. You can initialize it before calling the function. Call this function in your getting IDs method.
const getIdFromNodesWithChild = (node) => {
if (node.children != undefined){
ids.push(node.id)
const children_list = node.children
children_list.forEach( new_child => getIdFromNodesWithChild(new_child))
}}
caller function
const returnIds = (tree) => {
ids = []
getIdFromNodesWithChild(tree)
return (ids)
}
result : ['root', 'USER', 'ROLE', 'DASHBOARD', 'BRAND', 'COMPANY']

Recursive function creating too many copies of child nodes

I'm attempting to write a function to recursively delete nodes based on Parent-child relationships.
function recursiveDelete(parentNode, deleteStack) {
let toDelete = allNodes.filter(function (node) {
return node.Parent !== undefined && node.Parent[0] === parentNode.ID;
})
toDelete.forEach((childNode) => {
deleteStack.push(...recursiveDelete(childNode, toDelete));
});
return deleteStack;
}
When I run this code, it create arrays with multiple copies of each node to be deleted, which seems to correspond to the height of the tree. It still seems to be running in polynomial time, but it isn't ideal.
edit - below is an example of what gets returned when I run the function on allNodes, with the duplicates.
[
{
"ID": "recymaQKcdrGzAqRM",
"Name": "Depth 1",
"Type": "task",
"Duration": 0.1,
"Priority": "6",
"Parent": [
"recQT9BPQtqs7Cg0U"
],
"Predecessors": [
"recKwlRnVhKik3SZF"
],
"depth": 2
},
{
"ID": "recRcn1t2Nkupcb9L",
"Name": "Start",
"Type": "start",
"Duration": 0.1,
"Priority": "5",
"Parent": [
"recymaQKcdrGzAqRM"
]
},
{
"ID": "recBmEBC1bo1CcJ8i",
"Name": "Depth 2",
"Type": "task",
"Duration": 0.1,
"Priority": "5",
"Parent": [
"recymaQKcdrGzAqRM"
],
"Predecessors": [
"recRcn1t2Nkupcb9L"
]
},
{
"ID": "recRcn1t2Nkupcb9L",
"Name": "Start",
"Type": "start",
"Duration": 0.1,
"Priority": "5",
"Parent": [
"recymaQKcdrGzAqRM"
]
},
{
"ID": "recBmEBC1bo1CcJ8i",
"Name": "Depth 2",
"Type": "task",
"Duration": 0.1,
"Priority": "5",
"Parent": [
"recymaQKcdrGzAqRM"
],
"Predecessors": [
"recRcn1t2Nkupcb9L"
]
},
{
"ID": "rec7qUgu7kpnQ06s7",
"Name": "Start",
"Type": "start",
"Duration": 0.1,
"Priority": "5",
"Parent": [
"recBmEBC1bo1CcJ8i"
]
},
{
"ID": "rec5Lyx3zuAsCPv9W",
"Name": "Depth 3",
"Type": "task",
"Duration": 0.1,
"Priority": "5",
"Parent": [
"recBmEBC1bo1CcJ8i"
],
"Predecessors": [
"rec7qUgu7kpnQ06s7"
]
},
{
"ID": "rec7qUgu7kpnQ06s7",
"Name": "Start",
"Type": "start",
"Duration": 0.1,
"Priority": "5",
"Parent": [
"recBmEBC1bo1CcJ8i"
]
},
{
"ID": "rec5Lyx3zuAsCPv9W",
"Name": "Depth 3",
"Type": "task",
"Duration": 0.1,
"Priority": "5",
"Parent": [
"recBmEBC1bo1CcJ8i"
],
"Predecessors": [
"rec7qUgu7kpnQ06s7"
]
}
]
My current hack is to just remove duplicates:
[...new Set(recursiveDelete(allNode, [allNode]))]
But I'd like to figure out what I'm doing wrong. Thanks!
edit, I think I figured it out. Instead of passing toDelete to the recursive function, I should've only been passing [childNode], so the correct code looks like this:
function recursiveDelete(parentNode, deleteStack) {
let toDelete = allNodes.filter(function (node) {
return node.Parent !== undefined && node.Parent[0] === parentNode.ID;
});
toDelete.forEach((childNode) => {
deleteStack.push(...recursiveDelete(childNode, [childNode]));
});
return deleteStack;
}

Map the name of nodes onto links

This is my data and I am trying to map the name of the nodes to the sources and targets of the links.
var x = {
"nodes": [
{
"name": "Decision 3a"
},
{
"name": "Req 1"
},
{
"name": "Req 3c"
},
{
"name": "Cloud Services"
}
],
"links": [
{
"source": "0",
"target": "3",
"value": 100
},
{
"source": "4",
"target": "2",
"value": 100
}
]
};
I want the object to look like this for my visualization--
var x = {
"nodes": [
{
"name": "Decision 3a"
},
{
"name": "Req 3"
},
{
"name": "Req 3c"
},
{
"name": "Req 3b"
}
],
"links": [
{
"source": "Decision 3a",
"target": "Req 3c",
"value": 100
},
{
"source": "Cloud Services",
"target": "Req 1",
"value": 100
}
]
};
I tried adding the id to the nodes and then map them but in that case the id remain there in the nodes object.
you need to loop through the each object in the links array and get the index from the nodes array and assign that value to the links array.
i hope the below code solves the issue
var x = {
"nodes": [
{
"name": "Decision 3a"
},
{
"name": "Req 1"
},
{
"name": "Req 3c"
},
{
"name": "Req 4"
},
{
"name": "Cloud Services"
}
],
"links": [
{
"source": "0",
"target": "3",
"value": 100
},
{
"source": "4",
"target": "2",
"value": 100
}
]
};
x.links.forEach(o => {
let name = x.nodes[parseInt(o.source)].name;
o["source"] = name;
})
console.log("output",x)

Best way to transform data

I have a vendor API call in my nodejs app that returns various objects, with the below being an example of one object in the the JSON:
[{
"id": 365467865567,
"type": "PERSON",
"created_time": 1492809120,
"updated_time": 1492809542,
"last_contacted": 1492809128,
"properties": [
{
"type": "SYSTEM",
"name": "phone",
"value": "123456789"
},
{
"type": "SYSTEM",
"name": "first_name",
"value": "Test2"
},
{
"type": "SYSTEM",
"name": "last_name",
"value": "You"
},
{
"type": "CUSTOM",
"name": "utm_medium",
"value": "email"
}
]
}]
Its not very easy to use the data in this format, and need to transpose it to a key,value format. Something similar to:
[{
"id": 365467865567,
"type": "PERSON",
"created_time": 1492809120,
"updated_time": 1492809542,
"last_contacted": 1492809128,
"properties":
{
"phone": "123456789",
"first_name": "Test2",
"last_name": "You",
"utm_medium": "email"
}
}]
What would be the most efficient way to transform this?
Try looping over the properties and reassigning the variable.
var array = [{
"id": 365467865567,
"type": "PERSON",
"created_time": 1492809120,
"updated_time": 1492809542,
"last_contacted": 1492809128,
"properties": [{
"type": "SYSTEM",
"name": "phone",
"value": "123456789"
}, {
"type": "SYSTEM",
"name": "first_name",
"value": "Test2"
}, {
"type": "SYSTEM",
"name": "last_name",
"value": "You"
}, {
"type": "CUSTOM",
"name": "utm_medium",
"value": "email"
}]
}];
array.forEach(function(obj) {
var properties = {};
obj.properties.forEach(function(prop) {
properties[prop.name] = prop.value;
});
obj.properties = properties;
});
https://jsfiddle.net/hycg5r80/3/
here's a way, idk if most efficient but it's short n sweet.
thing[0].properties = thing[0].properties.reduce( (p, n) => (
n[p.name] = p.value;
return n;
), {});

Accessing data in an object and creating new object with filtered data

I am trying to search for specific nodes within an object and display a new object with the required data. There could be single or multiple fields in each composite type like displayed below.
This is the original object:
{
"section": "personal",
"fields": [
{
"type": "composite",
"name": "name",
"label": "Name",
"fields": [
{
"type": "text",
"name": "given",
"label": "First name",
"value": "Joe"
},
{
"type": "text",
"name": "family",
"label": "Last name",
"value": "Smith"
}
]
},
{
"type": "composite",
"name": "address",
"label": "Address",
"fields": [
{
"type": "text",
"name": "streetName",
"label": "Street Name",
"value": "1 High St"
},
{
"type": "text",
"name": "city",
"label": "City",
"value": "New York"
}
]
}
]
}
The resulting object should look like this:
{
name: {
given: "Joe",
family: "Smith",
},
address: {
streetName: "1 Hight St",
city: "New York"
}
}
EDIT*** Ideally, I'd like to figure out a way to use javascript methods (map/reduce/filter) and/or lodash to come up with an answer.
This is more an outline of what i've been looking at so far with built in methods.
var convertVals = function() {
var data = fields.fields;
var filter = data.filter(function(form) {
return form
})
.filter(function(form) {
return form.name && form.fields;
})
.map(function(form) {
return {form.name, [form.name.name]: form.name.value};
})
};
convertVals();
Thanks,
You can use Array.prototype.reduce() to iterate nested fields arrays. Set "name" property value of parent object as property name of object with value set to object set to "name" and "value" properties of objects within child "fields" array
var data = {
"section": "personal",
"fields": [
{
"type": "composite",
"name": "name",
"label": "Name",
"fields": [
{
"type": "text",
"name": "given",
"label": "First name",
"value": "Joe"
},
{
"type": "text",
"name": "family",
"label": "Last name",
"value": "Smith"
}
]
},
{
"type": "composite",
"name": "address",
"label": "Address",
"fields": [
{
"type": "text",
"name": "streetName",
"label": "Street Name",
"value": "1 High St"
},
{
"type": "text",
"name": "city",
"label": "City",
"value": "New York"
}
]
}
]
}
var res = data.fields.reduce((o, {name, fields}) =>
(o[name] = fields.reduce((curr, {name:key, value}) =>
(curr[key] = value, curr),{}), o), {});
console.log(res);
Well, here you go! Figured I might as well take a crack at it as no more questions were coming in.
var originalObject = {
"section": "personal",
"fields": [
{
"type": "composite",
"name": "name",
"label": "Name",
"fields": [
{
"type": "text",
"name": "given",
"label": "First name",
"value": "Joe"
},
{
"type": "text",
"name": "family",
"label": "Last name",
"value": "Smith"
}
]
},
{
"type": "composite",
"name": "address",
"label": "Address",
"fields": [
{
"type": "text",
"name": "streetName",
"label": "Street Name",
"value": "1 High St"
},
{
"type": "text",
"name": "city",
"label": "City",
"value": "New York"
}
]
}
]
};
function convertVals(obj){
var retObj = {};
for(var i=0;i<obj.fields.length;i++){
var tempObj={};
for(var j=0;j<obj.fields[i].fields.length;j++){
tempObj[obj.fields[i].fields[j].name] = obj.fields[i].fields[j].value;
}
retObj[obj.fields[i].name] = tempObj;
}
return retObj;
}
console.log(convertVals(originalObject));
/*
Should return:
{
name: {
given: "Joe",
family: "Smith",
},
address: {
streetName: "1 Hight St",
city: "New York"
}
}
*/
Here's a lodash solution that uses keyBy to assign the keys for each fields and mapValues to get the value for each field.
function getFields(data) {
return data.value || _(data.fields)
.keyBy('name')
.mapValues(getFields)
.value();
}
var data = {
"section": "personal",
"fields": [{
"type": "composite",
"name": "name",
"label": "Name",
"fields": [{
"type": "text",
"name": "given",
"label": "First name",
"value": "Joe"
},
{
"type": "text",
"name": "family",
"label": "Last name",
"value": "Smith"
}
]
},
{
"type": "composite",
"name": "address",
"label": "Address",
"fields": [{
"type": "text",
"name": "streetName",
"label": "Street Name",
"value": "1 High St"
},
{
"type": "text",
"name": "city",
"label": "City",
"value": "New York"
}
]
}
]
};
function getFields(data) {
return data.value || _(data.fields)
.keyBy('name')
.mapValues(getFields)
.value();
}
console.log(getFields(data));
body > div { min-height: 100%; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
There is a hidden implication in the solution above, if the value contains falsey values then it would dismiss this value instead and assume that there's a fields value. To solve this, we can use has to check if a value key exists and then perform the same operations that we did above.
function getFields(data) {
return _.has(data, 'value')? data.value:
_(data.fields)
.keyBy('name')
.mapValues(getFields)
.value();
}
var data = {
"section": "personal",
"fields": [{
"type": "composite",
"name": "name",
"label": "Name",
"fields": [{
"type": "text",
"name": "given",
"label": "First name",
"value": "Joe"
},
{
"type": "text",
"name": "family",
"label": "Last name",
"value": "Smith"
}
]
},
{
"type": "composite",
"name": "address",
"label": "Address",
"fields": [{
"type": "text",
"name": "streetName",
"label": "Street Name",
"value": "1 High St"
},
{
"type": "text",
"name": "city",
"label": "City",
"value": "New York"
}
]
}
]
};
function getFields(data) {
return _.has(data, 'value')? data.value:
_(data.fields)
.keyBy('name')
.mapValues(getFields)
.value();
}
console.log(getFields(data));
body > div { min-height: 100%; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

Categories

Resources