I have function that loops array and I have four if's - if it match I push value to output array:
const generate = (resources, resourceId) => {
let output = [];
for (let i = 0; i < resources.length; i++) {
if (resources[i].id === resourceId) {
if (CREATE & resources[i].privileges) {
output.push(CREATE);
}
if (READ & resources[i].privileges) {
output.push(READ);
}
if (UPDATE & resources[i].privileges) {
output.push(UPDATE);
}
if (DELETE & resources[i].privileges) {
output.push(DELETE);
}
}
}
return output;
};
I want to change this function to use map - is it possible? I try to do something like this:
const generateUsingMap = (resources, resourceId) => {
return resources.map((resource) => {
if (resource.id === resourceId) {
if (CREATE & resource.privileges) {
return CREATE;
}
if (READ & resource.privileges) {
return READ;
}
if (UPDATE & resource.privileges) {
return UPDATE;
}
if (UPDATE & resource.privileges) {
return UPDATE;
}
}
});
};
But in this case I will have only one value, because it returns from first if.
Maybe I need to use another function? I don't want to use for or forEach because in that cases I need to create unnecessary variable.
Update
My function is working in loop, function receive 2 arguments resources and resourceId.
For example variable resources contains:
{
"id": "1",
"name": "Test name 1",
"privileges": 1
},
{
"id": "2",
"name": "Test name 2",
"privileges": 2
},
{
"id": "3",
"name": "Test name 3",
"privileges": 8
},
{
"id": "4",
"name": "Test name 4",
"privileges": 0
},
{
"id": "5",
"name": "Test name 5",
"privileges": 15
}
]
Variable resourceId contains number (id) and receive severally values, for example on first iteration 1, for second 2 and so on.
For resources from example expected output will be:
[1]
[2]
[8]
[]
[1,2,4,8]
You can use reduce if both resource object id and privileges do not exist do not check further just return what you already accrued.
Only if both are present then check the CRUD operations.
const result = resources.reduce((output) => {
if (resources[i].id !== resourceId && !resources[i].privileges) {
return output;
}
if (CREATE) {
output.push(CREATE);
}
if (READ) {
output.push(READ);
}
if (UPDATE) {
output.push(UPDATE);
}
if (DELETE) {
output.push(DELETE);
}
return output;
}, [])
const generateUsingMap = (resources, resourceId) => {
return resources.filter(resource => resource.id === resourceId)
.map((resource) => {
if (CREATE & resource.privileges) {
return CREATE;
}
if (READ & resource.privileges) {
return READ;
}
if (UPDATE & resource.privileges) {
return UPDATE;
}
if (UPDATE & resource.privileges) {
return UPDATE;
}
});
};
Create an empty array resultArr. Iterate through resources via forEach and append options to it. Return the array at the end of the function
const generateUsingMap = (resources, resourceId) => {
const resultArr = [];
resources.forEach((resource) => {
if (resource.id === resourceId && resource.privileges) {
if (CREATE) {
resultArr.push(CREATE);
}
else if (READ) {
resultArr.push(READ);
}
else if (UPDATE) {
resultArr.push(UPDATE);
}
else if (DELETE) {
resultArr.push(DELETE);
}
}
});
return resultArr;
};
Related
api.get(`get-participant-reports/${this.userData.ind_id}`)
.then((res) => {
this.reportData = res.data;
for (var i = 0; i < this.reportData.length; i++) {
api.get(`get-not-eligible360/${this.reportData[i].id}`).then((res) => {
this.reportData.push(res.data)
console.log(this.reportData)
});
}
});
I have 2 api that I want to populate the same variable so I use .push() to insert the 2nd API call to the same variable.
This is what it looks like:
0:{
"id": 30,
"survey_template_name": "Big 5 Survey",
"report_template_name": "5 Step Profile Report",
"suborg_name": "Sandbox",
"program_name": "MNET Testing",
"iteration_name": "MNET2022 - Test July 2022",
}
1:{
"id": 3280,
"survey_template_name": "Big 5 Survey",
"report_template_name": "5 Step Profile Report",
"suborg_name": "Sandbox",
"program_name": "MNET Testing",
"iteration_name": "iteration-1a",
}
2:{
"0": {
"id": 30,
"not_eligible": "1",
}
}
3:{
"0": {
"id": 3280,
"not_eligible": "1",
}
}
What I want to happen is to get the not_eligible then push it into the existing variable that has the same id respectively. How can I do that?
I'm assuming that you have response data as array of object for both and need to add key not_eligible in first response data based on id of first response data so i have update code it should work for you.
api.get(`get-participant-reports/${this.userData.ind_id}`)
.then((res) => {
this.reportData = res.data;
for (var i = 0; i < this.reportData.length; i++) {
api.get(`get-not-eligible360/${this.reportData[i].id}`).then((res) => {
this.reportData.forEach(reportItem => {
const existData = res.data.find(resItem => resItem.id === reportItem.id);
if (existData) {
reportItem.not_eligible = existData.not_eligible
}
});
console.log(this.reportData)
});
}
});
Try with findIndex and if found set object property:
api.get(`get-participant-reports/${this.userData.ind_id}`)
.then((res) => {
this.reportData = res.data;
for (var i = 0; i < this.reportData.length; i++) {
api.get(`get-not-eligible360/${this.reportData[i].id}`).then((res) => {
let idx = this.reportData.findIndex(i => i.id == res['0'].id)
if(idx > -1) this.reportData[idx].not_eligible = res['0'].not_eligible
});
}
});
I am having json object like below which will be dynamic,
let data_existing= [
{
"client":[
{
"name":"aaaa",
"filter":{
"name":"123456"
}
}
]
},
{
"server":[
{
"name":"qqqqq",
"filter":{
"name":"984567"
}
}
]
},
]
From the inputs i will get an object like below,
let data_new = {
"client":[
{
"name":"bbbbb",
"filter":{
"name":"456789"
}
}
]
}
I need to append this object into the existing "client" json object. Expected output will be like,
[
{
"client":[
{
"name":"aaaa",
"filter":{
"name":"123456"
}
},
{
"name":"bbbb",
"filter":{
"name":"456789"
}
}
]
},
{
"server":[
{
"name":"qqqqq",
"filter":{
"name":"984567"
}
}
]
}
]
And, if the "data_new" is not exists in the main objects, it should as new objects like below, for example,
let data_new = {
"server2":[
{
"name":"kkkkk",
"filter":{
"name":"111111"
}
}
]
}
output will be like,
[
{
"client":[
{
"name":"aaaa",
"filter":{
"name":"123456"
}
},
]
},
{
"server":[
{
"name":"qqqqq",
"filter":{
"name":"984567"
}
}
]
},
{
"server2":[
{
"name":"kkkkk",
"filter":{
"name":"11111"
}
}
]
}
]
I tried the below method, but it is not working as expected. Some help would be appreciated.
Tried like below and not worked as expected,
function addData(oldData, newData) {
let [key, value] = Object.entries(newData)[0]
return oldData.reduce((op, inp) => {
if (inp.hasOwnProperty(key)) {
console.log("111");
op[key] = inp[key].concat(newData[key]);
} else {
console.log(JSON.stringify(inp));
op = Object.assign(op, inp);
}
return op
}, {})
}
Your function seems to work when the key already belongs to data_existing (e.g.: "client").
But you have to handle the second use-case: when the key was not found in the objects of data_existing (e.g.: "server2").
This shall be performed after the reduce loop, adding the new item to data_existing if the key was not found.
Here is an example of how you could achieve that:
function addData(inputData, inputItem) {
const [newKey, newValue] = Object.entries(inputItem)[0];
let wasFound = false; // true iif the key was found in list
const res = inputData.reduce((accumulator, item) => {
const [key, value] = Object.entries(item)[0];
const keyMatch = key === newKey;
if (keyMatch) {
wasFound = true;
}
// concatenate the lists in case of key matching
const newItem = { [key]: keyMatch ? [...value, ...newValue] : value };
return [...accumulator, newItem];
}, []);
if (!wasFound) {
res.push(inputItem); // if key was not found, add item to the list
}
return res;
}
Hope it helps.
I am studying the use of reduce in javascript, and I am trying to restructure an Array of Objects in a generic way - need to be dynamic.
flowchart - i get totaly lost
I started with this through.
Every ID becomes a Key.
Every PARENT identifies which Key it belongs to.
i have this:
const in = [
{
"id": "Ball",
"parent": "Futebol"
},
{
"id": "Nike",
"parent": "Ball"
},
{
"id": "Volley",
"parent": null
}
]
i want this
out = {
"Futebol": {
"Ball": {
"Nike": {}
}
},
"Volley": {}
}
i try it - and i had miserably failed.
const tree = require('./mock10.json')
// Every ID becomes a Key.
// Every PARENT identifies which Key it belongs to.
const parsedTree = {}
tree.reduce((acc, item) => {
if (parsedTree.hasOwnProperty(item.parent)){
if (parsedTree[`${item.parent}`].length > 0) {
parsedTree[`${item.parent}`][`${item.id}`] = {}
} else {
parsedTree[`${item.parent}`] = { [`${item.id}`]: {} }
}
} else {
// i get lost in logic
}
}, parsedTree)
console.log(parsedTree)
Got a working code for you, feel free to ask me about the implementation
Hope it helps :)
const arrSample = [
{
"id": "Ball",
"parent": "Futebol"
},
{
"id": "Nike",
"parent": "Ball"
},
{
"id": "Volley",
"parent": null
}
]
const buildTree = (arr) => {
return arr.reduce(([tree, treeMap], { id, parent }) => {
const val = {}
treeMap.set(id, val)
if (!parent) {
tree[id] = val
return [tree, treeMap]
}
if (!treeMap.has(parent)) {
const parentVal = { [id]: val }
treeMap.set(parent, parentVal)
tree[parent] = parentVal
return [tree, treeMap]
}
const newParentValue = treeMap.get(parent)
newParentValue[id] = val
treeMap.set(parent, newParentValue)
return [tree, treeMap]
}, [{}, new Map()])
}
const [result] = buildTree(arrSample)
console.log(JSON.stringify(result, 0, 2))
You could use reduce method for this and store each id on the first level of the object. This solution will work if the objects in the array are in the correct order as in the tree structure.
const data = [{"id":"Futebol","parent":null},{"id":"Ball","parent":"Futebol"},{"id":"Nike","parent":"Ball"},{"id":"Volley","parent":null}]
const result = data.reduce((r, { id, parent }) => {
if (!parent) {
r[id] = {}
r.tree[id] = r[id]
} else if (r[parent]) {
r[parent][id] = {}
r[id] = r[parent][id]
}
return r
}, {tree: {}}).tree
console.log(result)
If reduce solution is just an option, you can try this way:
var input = [
{
"id": "Ball",
"parent": "Futebol"
},
{
"id": "Nike",
"parent": "Ball"
},
{
"id": "Volley",
"parent": null
}
];
var output = {};
input.forEach(item => {
var temp = input.find(x => x.id === item.parent);
if (temp) {
temp[item.id] = {};
}
});
input = input.filter(item => !input.find(x => x.hasOwnProperty(item.id)));
input.forEach(item => {
if (!item.parent) {
output[item.id] = {};
} else {
for (var [id, value] of Object.entries(item)) {
if (typeof value === 'object') {
output[item.parent] = { [item.id]: { id: {} } };
}
}
}
})
console.log(output);
I have tried many things, but none works if we use an Array.prototype.reduce
As there are missing parents, and the elements are out of order, plus the fact that there can be an infinity of levels, I really do not believe that this question can be resolved with a simple reduce
This code should work whatever the cases :
- if all parents are not declared
- if there are infinitely many levels
- if they are in disorder
const origin =
[ { id: 'Ball', parent: 'Futebol' }
, { id: 'Nike', parent: 'Ball' }
, { id: 'Volley', parent: null }
, { id: 'lastOne', parent: 'level4' } // added
, { id: 'level4', parent: 'Nike' } // added
, { id: 'bis', parent: 'Nike' } // added
];
const Result = {} // guess who ?
, Parents = [] // tempory array to keep parents elements address by key names
;
let nbTodo = origin.length // need this one to verify number of elements to track
;
// set all the first levels, add a todo flags
origin.forEach(({id,parent},i,ori)=>
{
ori[i].todo = true // adding todo flag
if (parent===null)
{
Result[id] = {} // new first level element
ori[i].todo = false // one less :)
nbTodo--
Parents.push(({ref:id,path:Result[id]}) ) // I know who you are!
}
else if (origin.filter(el=>el.id===parent).length===0) // if he has no parent...
{
Result[parent] = {} // we create it one
Parents.push({ref:parent,path:Result[parent]} )
}
})
// to put the children back in their parents' arms
while(nbTodo>0) // while there are still some
{
origin.forEach(({id,parent,todo},i,ori)=> // little by little we find them all
{
if(todo) // got one !
{
let pos = Parents.find(p=>p.ref===parent) // have parent already been placed?
if(pos)
{
ori[i].todo = false // to be sure not to repeat yourself unnecessarily
nbTodo-- // one less :)
pos.path[id] = {} // and voila, parentage is done
Parents.push(({ref:id,path:pos.path[id]}) ) // he can now take on the role of parent
}
}
})
}
for (let i=origin.length;i--;) { delete origin[i].todo } // remove todo flags
console.log( JSON.stringify(Result, 0, 2) )
.as-console-wrapper { max-height: 100% !important; top: 0; }
I finaly made this one, based on this previous on, and done with a first step by a reduce...
to by pass the Array of Parents, I made a recursive function for searching each parent elements thru the levels of parsedTree result.
here is the code:
const Tree =
[ { id: 'Ball', parent: 'Futebol' }
, { id: 'Nike', parent: 'Ball' }
, { id: 'Volley', parent: null }
, { id: 'lastOne', parent: 'level4' } // added
, { id: 'level4', parent: 'Nike' } // added
, { id: 'bis', parent: 'Nike' } // added
];
const parsedTree = Tree.reduce((parTree, {id,parent},i ) => {
Tree[i].todo = false
if (parent===null)
{ parTree[id] = {} }
else if (Tree.filter(el=>el.id===parent).length===0) // if he has no parent...
{ parTree[parent] = { [id]: {} } }
else
{ Tree[i].todo = true }
return parTree
}, {})
function parsedTreeSearch(id, part) {
let rep = null
for(let kId in part) {
if (kId===id)
{ rep = part[kId] }
else if (Object.keys(part[kId]).length)
{ rep = parsedTreeSearch(id, part[kId]) }
if (rep) break
}
return rep
}
while (Boolean(Tree.find(t=>t.todo))) {
Tree.forEach(({id,parent,todo},i)=>{ // little by little we find them all
if (todo) {
let Pelm = parsedTreeSearch(parent, parsedTree)
if (Boolean(Pelm)) {
Pelm[id] = {}
Tree[i].todo = false
} } }) }
for (let i=Tree.length;i--;) { delete Tree[i].todo } // remove todo flags
console.log( JSON.stringify( parsedTree ,0,2))
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have an object of nested route.
Any route MAY contains a list of route childRoutes.
I want to get the list of all the route that contains the key menu.
const routes = [{
"name": "userManagement",
"childRoutes": [
{
"name": "blogManagement",
"childRoutes": [
{
"name": "blog", // <=== I want to have this route
"menu": {
"role": 1020
}
}
],
},
{
"name": "organizationList", // <=== and this one
"menu": {
"role": 1004
}
}
],
}, {
"name": "test",
"menu": { "role": 4667 }
}];
const deepFlatten = arr => [].concat(...arr.map(v => (Array.isArray(v) ? deepFlatten(v) : v)));
// Should handle nesting of route
const links = deepFlatten(routes).filter((r) => !!r.menu);
console.log('it should have a length of 3:', links.length === 3);
console.log('it should be blog:', links[0].name === 'blog');
console.log('it should be organizationList:', links[1].name === 'organizationList');
console.log('it should be test:', links[2].name === 'test');
The above snippet does not work recursively yet.
How can I do it recursively without any third-party library ?
#yBrodsky's answer can be adapted to isolate and exhibit the generic flatMap operation – here, you'll see that the routes flattened with much of the reduce-map-concat plumbing out of the programmer's way.
// polyfill if you don't have it
Array.prototype.flatMap = function (f)
{
return this.reduce ((acc, x) =>
acc.concat (f (x)), [])
}
// your data
const routes =
[ { name : "userManagement"
, childRoutes :
[ { name : "blogManagement"
, childRoutes :
[ { name : "blog"
, menu : { role : 1020 }
}
]
}
, { name : "organizationList"
, menu : { role : 1004 }
}
]
}
, { name : "test"
, menu : { role : 4667 }
}
]
// flat-mapped routes
const allChildRoutes =
routes.flatMap (function loop (node) {
if (node.childRoutes)
return node.childRoutes.flatMap (loop)
else
return [node]
})
console.log (allChildRoutes)
how about this, seems to work.
const flatten = (routes) => {
return routes.reduce((acc, r) => {
if(r.childRoutes && r.childRoutes.length) {
acc = acc.concat(flatten(r.childRoutes));
} else {
acc.push(r);
}
return acc;
}, [])
}
https://jsfiddle.net/vv9odcxw/
I'm trying to filter a users JSON via JavaScript's filter, map, and reduce methods. However I cannot get the exact result I pretend.
var users = {
"fooUser": {
"apps": [
{
"id": "7i2j3bk85"
},
{
"id": "o8yasg69h"
}
]
},
"barUser": {
"apps": [
{
"id": "789gbiai7t"
}
]
}};
The logic is: I only know the AppId (and not the User it belogs to), so I'd have to map/filter each User, and return it ONLY if it has that Appid (and return ONLY that AppId).
var filteredApps = Object.keys(users).filter(function (element, index, array) {
var exists = users[element].apps.filter(function (element, index, array) {
if (element.id === 'o8yasg69h') {
return true;
} else {
return false;
}
});
if (exists[0]) {
return true;
} else {
return false;
}
}).map(function (item, index, array) {
return users[item].apps;
});
console.log(filteredApps);
I obtain (a multiArray with no-filtered Apps):
[[
{
id: "7i2j3bk85"
},
{
id: "o8yasg69h"
}
]]
But I would like to obtain (one plain Object, with the filtered App):
{
id: "o8yasg69h"
}
You can do this with the following one-liner:
[].concat(...Object.keys(users).map(x=> users[x].apps)).find(x=> x.id === "o8yasg69h")
To expand it a bit:
[].concat(... // flattens the array of apps
Object.keys(users) // gets the keys of users
.map(x=> users[x].apps) // maps the array to the apps of the user
).find(x=> x.id === "o8yasg69h") // finds app which id is "o8yasg69h"
I'd do it with reduce and ES6 find:
function searchById(id){
return Object.keys(users).reduce(function(result, user){
return result ? result : users[user].apps.find(function(obj){
return obj.id === id;
});
}, false);
}