I need to reduce the given object into some datastructure. This is my input object.
const receiver = {
USER1: {
module: ['a_critical', 'a_normal','b_normal']
},
USER2: {
module: ['a_critical', 'a_normal','b_critical']
},
USER3: {
module: ['a_critical']
}
};
const allModules = ['a_normal', 'a_critical', 'b_normal', 'b_critical'];
Desired output:
{
"a_critical": [
{
"user": [
"USER1", "USER2", "USER3"
]
}
],
"a_normal": [
{
"user": [
"USER1", "USER2"
]
}
],
"b_normal": [
{
"user": [
"USER1"
]
}
],
"b_critical": [
{
"user": [
"USER2"
]
}
]
}
I have tried doing but i was getting some problems. I am getting some duplicate properties which should be there. I can share the code on what i have tried.
const receiverObj = {};
let count = 0;
Object.keys(receiver).forEach((item) => {
receiver[item].module.forEach((module) => {
if(allModules.includes(module)) {
count = 1;
if(count) {
receiverObj[module] = [];
receiverObj[module].push({user: [item] });
}
receiverObj[module].push({user: item });
count = 0;
}
})
});
console.log(JSON.stringify(receiverObj, null, 2));
Actual result which i got:
{
"a_critical": [
{
"user": [
"USER3"
]
},
{
"user": "USER3"
}
],
"a_normal": [
{
"user": [
"USER2"
]
},
{
"user": "USER2"
}
],
"b_normal": [
{
"user": [
"USER1"
]
},
{
"user": "USER1"
}
],
"b_critical": [
{
"user": [
"USER2"
]
},
{
"user": "USER2"
}
]
}
Is there any optimal way of doing this ? can someone help ?
Iterate over each module in a reduce callback, creating a { user: [] } object in the accumulator if it doesn't exist yet, and then push to that array:
const receiver = {
USER1: {
module: ['a_critical', 'a_normal','b_normal']
},
USER2: {
module: ['a_critical', 'a_normal','b_critical']
},
USER3: {
module: ['a_critical']
}
};
const output = Object.entries(receiver)
.reduce((a, [user, { module }]) => {
module.forEach((name) => {
if (!a[name]) {
a[name] = { user: [] };
}
a[name].user.push(user);
});
return a;
}, {});
console.log(output);
You could also create the accumulator object in advance, if you wanted, since you have allModules, thereby avoiding conditionals inside the .reduce:
const receiver = {
USER1: {
module: ['a_critical', 'a_normal','b_normal']
},
USER2: {
module: ['a_critical', 'a_normal','b_critical']
},
USER3: {
module: ['a_critical']
}
};
const allModules = ['a_normal', 'a_critical', 'b_normal', 'b_critical'];
const accum = Object.fromEntries(
allModules.map(
name => [name, { user: [] }]
)
);
const output = Object.entries(receiver)
.reduce((a, [user, { module }]) => {
module.forEach((name) => {
a[name].user.push(user);
});
return a;
}, accum);
console.log(output);
Use Array.prototype.reduce()
const receiver = {
USER1: {
module: ['a_critical', 'a_normal','b_normal']
},
USER2: {
module: ['a_critical', 'a_normal','b_critical']
},
USER3: {
module: ['a_critical']
}
}
const allModules = ['a_normal', 'a_critical', 'b_normal', 'b_critical']
const result = allModules.reduce((modulesObj, moduleName) => {
modulesObj[moduleName] = [{ user: [] }]
for (let user in receiver) {
if (receiver[user].module.includes(moduleName)) {
modulesObj[moduleName][0].user.push(user)
}
}
return modulesObj
}, {})
console.log(result)
Try this one
const receiver = {
USER1: {
module: ['a_critical', 'a_normal','b_normal']
},
USER2: {
module: ['a_critical', 'a_normal','b_critical']
},
USER3: {
module: ['a_critical']
}
};
const userByModules = Object.keys(receiver).reduce(function (acc, user) {
receiver[user].module.forEach((module) => {
if (acc[module]) {
acc[module].user.push(user);
} else {
acc[module] = {user: [user]};
}
});
return acc;
}, {});
console.log(userByModules);
const receiver = {
USER1: {
module: ["a_critical", "a_normal", "b_normal"]
},
USER2: {
module: ["a_critical", "a_normal", "b_critical"]
},
USER3: {
module: ["a_critical"]
}
};
const allModules = ["a_normal", "a_critical", "b_normal", "b_critical"];
let reduce = () => {
// create output object
let output = {};
// add modules into output
allModules.map(mod => {
return (output[mod] = [{ 'user': []}]);
});
// map users to modules
for (let userKey in receiver) {
let userModules = receiver[userKey].module;
userModules.forEach(mod => {
if(output.hasOwnProperty(mod)) {
output[mod][0]['user'].push(userKey);
}
});
}
};
reduce();
Straightforward way of getting the job done. No fancy functions used.Hopefully this logic would be easy to follow.
The below snippet reduce the provided allModules array into the expected result. However, I think this structure
"a_normal": {
"user": [
"USER1",
"USER2"
]
},
...
make sense more than the below snippet because you have to access the first index of each module to get user value
"a_normal": [
{
"user": [
"USER1",
"USER2"
]
}
],
const receiver = {
USER1: { module: ["a_critical", "a_normal", "b_normal"] },
USER2: { module: ["a_critical", "a_normal", "b_critical"] },
USER3: { module: ["a_critical"] }
};
const allModules = ["a_normal", "a_critical", "b_normal", "b_critical"];
const result = allModules.reduce((prevResult, curModule, index) => {
if(!prevResult[curModule]) {
prevResult[curModule]=[{user:[]}];
}
Object.keys(receiver).forEach((user) => {
if(receiver[user].module.includes(curModule) &&
!prevResult[curModule][0].user.includes(user)) {
prevResult[curModule][0].user.push(user);
}
});
return prevResult;
},{});
console.log(JSON.stringify(result, null, 2));
Related
I need to restructure a nested JSON data.
Here is how it looks like:
{
"MainKey1": [
{
"Section1": {
"ParentTag1 Mapped Label": {
"ParentTag1": {
"Tag1 Mapped Label": {
"Tag1": "1234567890"
}
}
}
}
},
{
"Section2": {
"ParentTag1 Mapped Label": {
"ParentTag1": {
"Tag1 Label": {
"Tag1": "111222333444"
},
"Tag2 Label": {
"Tag2": "121212"
},
"Tag3 Label": {
"Tag3": "0987654321"
}
}
}
}
}
],
"MainKey2": [
{
"Section1": {
"ParentTag1 Mapped Label": {
"ParentTag1": {
"Tag1 Mapped Label": {
"Tag1": "1234567890"
}
}
}
}
}
]
}
And this is a sample of the converted JSON:
{
MainKey: [
{
Section1: [
{
ParentTag1: [
{ Tag1: "1234567890" }
]
}
]
},
{
Section2: [
{
ParentTag1: [
{ Tag1: "111222333444" },
{ Tag2: "121212" },
{ Tag3: "0987654321" }
]
}
]
}
],
MainKey2: [
{
Section1: [
{
ParentTag1 : [
{ Tag1: "1234567890" }
]
}
]
}
]
}
Rules:
Everything inside a MainKey (outermost keys, could be any name) should be an array
All labels should be stripped (as the label could be any name, without the actual word "Label", we can determine if it is a label based on the depth level. Since the JSON will have the label as the parent and the actual "tag" as a child.
Here is what I currently have (it is a mess, sorry!)
function convertJson (jsonObj) {
const mainKeys = Object.keys(jsonObj)
let output = {}
for (let i = 0; i < mainKeys.length; i++) {
const mainKey = mainKeys[i]
let result = []
output[mainKey] = result
for (let j = 0; j < jsonObj[mainKey].length; j++) {
const innerObj = {...jsonObj[mainKey][j]}
const sectionName = Object.keys(innerObj)[0]
const sectionObj = {}
sectionObj[sectionName] = []
const index = result.push(sectionObj) - 1
parseObj(innerObj[sectionName], result[index], 0) // if I change 2nd param to: result, it generates incorrect output
}
}
console.log(output)
}
function parseObj (innerObj, result, depthCount) {
for (var key in innerObj) {
if (typeof innerObj[key] === "object") {
if (depthCount % 2 === 1) {
const parentObj = {}
parentObj[key] = []
result.push(parentObj)
depthCount++
parseObj(innerObj[key], parentObj[key], depthCount)
} else {
depthCount++
parseObj(innerObj[key], result, depthCount)
}
} else {
const keyValuePairObj = {}
keyValuePairObj[key] = innerObj[key]
result.push(keyValuePairObj)
}
}
return result
}
convertJson(json)
But it generates an error:
Uncaught TypeError: result.push is not a function
Now if I change line 90 from:
parseObj(innerObj[sectionName], result[index], 0)
to:
parseObj(innerObj[sectionName], result, 0)
Here is incorrect output:
{
"MainKey1": [
{
"Section1": []
},
{
"ParentTag1": [
{
"Tag1": "1234567890"
}
]
},
{
"Section2": []
},
{
"ParentTag1": [
{
"Tag1": "111222333444"
},
{
"Tag2 Label": [
{
"Tag2": "121212"
}
]
},
{
"Tag3": "0987654321"
}
]
}
],
"MainKey2": [
{
"Section1": []
},
{
"Tag1": "1234567890"
}
]
}
And here is my fiddle:
https://jsfiddle.net/kzaiwo/L4avxmyd/36/
Thanks a lot! Appreciate any help!
I have an array of objects, where each object has a property (parentsList) indicating the category the current item belongs to, something like:
const data = [
{
...other properties,
"parentsList": [
"Assets",
"Icons"
],
},
{
...other properties,
"parentsList": [
"Assets",
"Fonts"
],
},
{
...other properties,
"parentsList": [
"Programming",
"JavaScript",
"Docs"
],
},
{
...other properties,
"parentsList": [
"Programming",
"JavaScript",
"React",
"Libraries",
],
},
]
That means the first object belongs to assets/icons, the second to assets/fonts, third to programming/javascript/docs and so on.
I'm trying to map it to a tree-like view, where siblings should be under the same parent, something like:
const data = [
{
name: 'Assets',
id: 'assets',
children: [
{
name: 'Icons',
id: 'assets/icons',
},
{
name: 'Illustrations',
id: 'assets/illustrations',
},
],
},
{
name: 'Programming',
id: 'programming',
children: [
{
name: 'JavaScript',
id: 'programming/javascript',
children: [
{
name: 'Docs',
id: 'programming/javascript/docs',
},
{
name: 'React',
id: 'programming/javascript/react',
children: [
{
name: 'Libraries',
id: 'programming/javascript/react/libraries',
},
],
},
],
},
],
},
]
I imagine it's gonna be easier to traverse from the right, maybe with reduceRight(), but I can't seem to get it right.
Anyone would know how to achieve that?
Thanks!
I tend to avoid using reduce because I find it difficult to read the code that has reduce in it. So, here is a non-reduce way.
const data = [
{
parentsList: [
"Assets",
"Icons"
],
},
{
parentsList: [
"Assets",
"Fonts"
],
},
{
parentsList: [
"Programming",
"JavaScript",
"Docs"
],
},
{
parentsList: [
"Programming",
"JavaScript",
"React",
"Libraries",
],
},
];
const processedData = [];
for (const item of data) {
const parents = [...item.parentsList].reverse();
let children = processedData;
const ids = [];
while (parents.length > 0) {
const parent = parents.pop();
ids.push(parent.toLowerCase());
let foundParent = false;
for (const child of children) {
if (child.name === parent) {
children = child.children;
foundParent = true;
break;
}
}
if (!foundParent) {
const newChild = {name: parent, id: ids.join("/"), children: [],};
children.push(newChild);
children = newChild.children;
}
}
}
console.log(processedData);
You can do this as a combination of forEach and reduce and create a nested hierarchy based on the parentsList array.
const data = [{"parentsList":["Assets","Icons"]},{"parentsList":["Assets","Fonts"]},{"parentsList":["Programming","JavaScript","Docs"]},{"parentsList":["Programming","JavaScript","React","Libraries"]}]
const result = []
data.forEach(function({ parentsList, ...rest }) {
let id = '';
parentsList.reduce((r, name, i) => {
id += (id.length ? '/' : '') + name.toLowerCase();
if (!r[name]) {
const value = { id, name }
r[name] = {result: []}
if (i != parentsList.length - 1) {
value.children = r[name].result
} else {
Object.assign(value, rest)
}
r.result.push(value)
}
return r[name]
}, this)
}, {result})
console.log(result)
A short approach by using nested objects as hash tables.
const
data = [{ parentsList: ["Assets", "Icons"] }, { parentsList: ["Assets", "Fonts"] }, { parentsList: ["Programming", "JavaScript", "Docs"] }, { parentsList: ["Programming", "JavaScript", "React", "Libraries"] }],
tree = data.reduce((t, { parentsList }) => {
parentsList.reduce((r, name, i, a) => {
const id = a.slice(0, i + 1).join('/').toLowerCase();
if (!r[name]) {
r[name] = { _: { name, id } };
(r._.children ??= []).push(r[name]._);
}
return r[name];
}, t);
return t;
}, { _: {} })._.children;
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I am struggling to add a field to an map in an array. I am trying to add "canAssist": false to each map in the array for each of the countries.
Here is my database:
[
{
"Afghanistan": {
"country": "Afghanistan",
"countryCode": "AF",
"countryCodeAlt": "AFG",
"emoji": "🇦🇫",
"packages": [
{
"name": "Luxury Couple",
"cost": "$2000.00",
// I want to add canAssist:false here!
},
{
"name": "Quick Retreat",
"cost": "$1000.00",
// I want to add canAssist:false here!
}
]
}
},
{...}
{...}
]
This is what I've tried:
let travelData = database.collection('countries').doc(docName);
travelData.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(array) {
packages.map(package => {
return package.add({
canAssist: false
});
})
});
});
You can use Object.values() and object destructuring to achieve this.
const querySnapshot = [
{
Afghanistan: {
country: 'Afghanistan',
countryCode: 'AF',
countryCodeAlt: 'AFG',
emoji: '🇦🇫',
packages: [
{
name: 'Luxury Couple',
cost: '$2000.00',
// I want to add canAssist:false here!
},
{
name: 'Quick Retreat',
cost: '$1000.00',
// I want to add canAssist:false here!
},
],
},
},
{
...
},
{
...
},
];
const updateSnapshot = (snapshot, newData) => {
return snapshot.map(countryData => {
// only one field with the name of the country
const country = Object.values(countryData)[0];
let updatedCountry = { ...country };
const field = country[newData.field];
if (field) {
if (typeof field === 'string') {
updatedCountry[newData.field] = newData.values;
} else if (Array.isArray(field)) {
updatedCountry[newData.field] = field.map(data => ({ ...data, ...newData.values }));
}
}
return { [updatedCountry.country]: updatedCountry };
});
};
(() => {
console.log('Original', JSON.stringify(querySnapshot, null, 4));
const updatedSnapshot = updateSnapshot(querySnapshot, { field: 'packages', values: { canAssist: false } });
console.log('Updated', JSON.stringify(updatedSnapshot, null, 4));
const updatedSnapshot2 = updateSnapshot(querySnapshot, { field: 'emoji', values: '🇪🇸' });
console.log('Spanish!', JSON.stringify(updatedSnapshot2, null, 4));
})();
Of course, you don't need to have that dynamism with the 'newData', I just added in case you want to play around any field of your datasource.
How can I get the names of different activity in an array by using map function in this type of response. So that in a new array, assume that activity[] i will get names of all the activities mention below.
if the array is
const response = [
{
"Groups" : {
"Roles" : {
"submission": {
"subject" : {
"name": "history",
}
}
}
}
}
];
I managed to do this using an IIFE but there may be cleaner ways
assuming there in one object in the array and no other path to other permission
const response = [
{
"Roles" : {
"Permission" : {
"PERMISSION1": {
"Activity" : {
"name": "Manage Clients",
}
},
"PERMISSION2": {
"Activity" : {
"name": "Manage Users",
}
}
}
}
}
];
let activities = (() => {
let res = []
for (let perm in response[0].Roles.Permission) {
for (let act in response[0].Roles.Permission[perm]) {
res.push(response[0].Roles.Permission[perm][act].name)
}
}
return res})()
console.log(activities)
At first, you should convert Permission object to array, cause object doesn't have method map.
Then you could use map function where you can collect all your permissions' names for every item in response
const response = [{
"Roles": {
"Permission": {
"PERMISSION1": {
"Activity": {
"name": "Manage Clients",
}
},
"PERMISSION2": {
"Activity": {
"name": "Manage Users",
}
}
}
}
}];
response.forEach((item) => {
item.Activities = Object.values(item.Roles.Permission).map((permission) => permission.Activity.name)
});
alert(JSON.stringify(response));
The only array you have is response. If each item in response has a Roles that has a Permission that has several keys with objects that have Activity with name then you can do the following:
var response = [
{
Roles: {
Permission: {
PERMISSION1: {
Activity: {
name: 'Manage Clients',
},
},
PERMISSION2: {
Activity: {
name: 'Manage Users',
},
},
},
},
},
];
console.log(
response.map(
(item) =>
Object.values(item.Roles.Permission)
.map(
(permission) => permission.Activity.name
)
)
);
I recommend using a flatMap, so use .reduce.
const response = [{
"Roles": {
"Permission": {
"PERMISSION1": {
"Activity": {
"name": "Manage Clients",
}
},
"PERMISSION2": {
"Activity": {
"name": "Manage Users",
}
}
}
}
}];
const activityNames = response.reduce(function (acc, res) {
const permissions = res.Roles.Permission;
const permissionKeys = Object.keys(permissions);
const names = permissionKeys.map(function(permissionKey) {
return permissions[permissionKey].Activity.name;
});
acc.push(...names);
return acc;
}, [])
console.log(activityNames); // ["Manage Clients", "Manage Users"]
Copying data does not return correct results. This code looks fine, but it isn't giving me the results which are correct. Am I missing something? Looks as though it's using the same instance to copy the data into the object.
let sidList = [
{ taskTargetHosts: [['host1'], ['host1']] },
{ taskTargetHosts: [['host3'], ['host3']] },
];
let jobData = [
{ config: { tasks: [{ targetHosts: [] }, { targetHosts: [] }] } },
{
config: {
tasks: [{ targetHosts: [] }, { targetHosts: [] }],
},
},
];
let dataIndx = 0;
for (let sidRow of sidList) {
for (
let taskIndx = 0;
taskIndx < jobData[dataIndx].config.tasks.length;
taskIndx++
) {
jobData[dataIndx].config.tasks[taskIndx].targetHosts =
sidRow.taskTargetHosts[taskIndx];
}
dataIndx++;
}
output expected for jobData:
[{"config": {"tasks": [{"targetHosts": ["host1"]},{"targetHosts": ["host1"]}]}},{"config": {"tasks": [{"targetHosts": ["host3"]},{"targetHosts": ["host3"]}]}}]
What I'm getting:
[{"config": {"tasks": [{"targetHosts": ["host1"]},{"targetHosts": ["host1"]}]}},{"config": {"tasks": [{"targetHosts": ["host1"]},{"targetHosts":["host1"]}]}}]
it seems to work for me your problem may be elsewhere
let sidList = [
{ taskTargetHosts: [['host1'], ['host1']] },
{ taskTargetHosts: [['host3'], ['host3']] },
];
let jobData = [
{ config: { tasks: [{ targetHosts: [] }, { targetHosts: [] }] } },
{ config: { tasks: [{ targetHosts: [] }, { targetHosts: [] }] } },
];
let dataIndx = 0;
for (let sidRow of sidList) {
for ( let taskIndx = 0; taskIndx < jobData[dataIndx].config.tasks.length; taskIndx++ ) {
jobData[dataIndx].config.tasks[taskIndx].targetHosts = sidRow.taskTargetHosts[dataIndx];
}
dataIndx++;
}
console.log (jobData[0].config.tasks[0]); //{ targetHosts: [ 'host1' ] }
console.log (jobData[0].config.tasks[1]); //{ targetHosts: [ 'host1' ] }
console.log (jobData[1].config.tasks[0]); //{ targetHosts: [ 'host3' ] }
console.log (jobData[1].config.tasks[1]); //{ targetHosts: [ 'host3' ] }