push object to an array within multiple loops - javascript

I have an array of objects like this [ { category: 'xxx' }, { author: '12345' } ]. My task is search this array and find in mongoDB to return _id [ { category: '_id' }, { author: '_id' } ]
I have code to do this, I create an empty array arrSearch and when it found the _id, it will push to this array. But after running, the array is empty, no data pushed. I'm not sure what wrong with it.
export const getObjIdFromCondition = async (conditionArr) => {
var arrSearch = []
try {
await conditionArr.map(obj => {
for (let key in obj) {
switch (key) {
case 'category':
const foundCategory = Category.findOne({ slug: obj[key] }).orFail()
foundCategory.then(resp => arrSearch.push({category: resp._id})) // push obj to array
break
case 'author':
const foundAuthor = Author.findOne({ author_id: obj[key] }).orFail()
foundAuthor.then(resp => arrSearch.push({ author: resp._id }))
break
default:
break
}
}
})
} catch (error) {
console.log(error);
}
console.log('arrSearch', arrSearch) // -> empty array
}
Can you help me? Thank you in advanced.

I try to use promise all and it works.
export const getObjIdFromCondition = async (conditionArr) => {
try {
var arrSearch = await Promise.all(
conditionArr.map(async (obj) => {
for (let key in obj) {
switch (key) {
case 'category':
const foundCategory = await Category.findOne({ slug: obj[key] })
return { category: foundCategory._id }
case 'author':
const foundAuthor = await Author.findOne({ author_id: obj[key] })
return { author: foundAuthor._id }
default:
break
}
}
})
)
} catch (error) {
console.log(error)
}
console.log("arrSearch", arrSearch)
}

Related

Calling recursive function in loop with async/await and Promise.all

I have a use case where I'm trying to loop through an array of objects, where I need to make some GraphQL requests that may have some pagination for a given object in the array. I'm trying to speed up performance by pushing the recursive function to an array of promises, and then use Promse.all to resolve all of those.
I'm running into an issue though where I'm getting an undefined response from Promise.all - The end goal is to have the following response for each unique object in the array:
[{
account: test1,
id: 1,
high: 2039,
critical: 4059
},
{
account: test2,
id: 2,
high: 395,
critical: 203
}]
...where I'm only returning anAccount object after recursion is done paginating/making all requests for a given account object.
Here is the sample code:
const fetch = require('isomorphic-fetch');
const API_KEY = '<key>';
async function main() {
let promises = [];
let accounts = [{'name': 'test1', 'id': 1}, {'name': 'test2' , 'id': 2}];
for (const a of accounts) {
let cursor = null;
let anAccountsResults = [];
promises.push(getCounts(a, anAccountsResults, cursor));
}
let allResults = await Promise.all(promises);
console.log(allResults);
}
async function getCounts(acct, results, c) {
var q = ``;
if (c == null) {
q = `{
actor {
account(id: ${acct.id}) {
aiIssues {
issues(filter: {states: ACTIVATED}) {
issues {
issueId
priority
}
nextCursor
}
}
}
}
}`
} else {
q = `{
actor {
account(id: ${acct.id}) {
aiIssues {
issues(filter: {states: ACTIVATED}, cursor: "${c}") {
issues {
issueId
priority
}
nextCursor
}
}
}
}
}`
}
const resp = await fetch('https://my.api.com/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'API-Key': API_KEY
},
body: JSON.stringify({
query: q,
variables: ''}),
});
let json_resp = await resp.json();
let aSingleResult = json_resp.data.actor.account.aiIssues.issues.issues;
let nextCursor = json_resp.data.actor.account.aiIssues.issues.nextCursor;
console.log(nextCursor);
if (nextCursor == null) {
results = results.concat(aSingleResult);
} else {
results = results.concat(aSingleResult);
await getCounts(acct, results, nextCursor);
}
let criticalCount = results.filter(i => i.priority == 'CRITICAL').length;
let highCount = results.filter(i => i.priority == 'HIGH').length;
let anAccount = {
account: acct.name,
id: acct.id,
high: highCount,
critical: criticalCount
};
return anAccount;
}
main();
logging anAccount in function getCounts has the correct detail, but when returning it, logging the output of Promise.all(promises) yields undefined. Is there a better way to handle this in a way where I can still asynchronously run multiple recursive functions in parallel within the loop with Promise.all?
Your main problem appears to be that results = results.concat(aSingleResult); does not mutate the array you passed, but only reassigns the local variable results inside the function, so the anAccount only will use the aSingleResult from the current call.
Instead of collecting things into a results array that you pass an a parameter, better have every call return a new array. Then in the recursive await getCounts(acct, results, nextCursor) call, do not ignore the return value.
async function main() {
let promises = [];
const accounts = [{'name': 'test1', 'id': 1}, {'name': 'test2' , 'id': 2}];
const promises = accounts.map(async acct => {
const results = await getIssues(acct);
const criticalCount = results.filter(i => i.priority == 'CRITICAL').length;
const highCount = results.filter(i => i.priority == 'HIGH').length;
return {
account: acct.name,
id: acct.id,
high: highCount,
critical: criticalCount
};
});
const allResults = await Promise.all(promises);
console.log(allResults);
}
const query = `query ($accountId: ID!, $cursor: IssuesCursor) {
actor {
account(id: $accountId) {
aiIssues {
issues(filter: {states: ACTIVATED}, cursor: $cursor) {
issues {
issueId
priority
}
nextCursor
}
}
}
}
}`;
async function getIssues(acct, cursor) {
const resp = await fetch('https://my.api.com/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'API-Key': API_KEY
},
body: JSON.stringify({
query: q,
variables: {
accountId: acct.id,
cursor,
}
}),
});
if (!resp.ok) throw new Error(resp.statusText);
const { data, error } = await resp.json();
if (error) throw new Error('GraphQL error', {cause: error});
const { nextCursor, issues } = data.actor.account.aiIssues.issues;
if (nextCursor == null) {
return issues;
} else {
return issues.concat(await getIssues(acct, nextCursor));
}
}

Javascript combine object

I'm trying to transform the object received from the to a format used in backend.
I receive this object
{
'User.permissions.user.view.dashboard': true,
'Admin.permissions.user.view.dashboard': true,
'Admin.permissions.admin.view.dashboard': true,
}
The first part of the key (User, Admin) is the role name, the rest is the role. I need to combine this object into an object that has role names as keys for arrays containing the permission strings. The final result should look like this
{
'User': [
'permissions.user.view.dashboard'
],
'Admin': [
'permissions.user.view.dashboard',
'permissions.user.admin.dashboard;
]
}
So far I managed to get this result, but I'm not sure how to combine the results
const data = JSON.parse(req.body)
const permissions = Object.keys(data).map((key) => {
const permParts = key.split('.')
return {[permParts[0]]: permParts.slice(1).join('.')}
})
console.log(permissions);
[
{ User: 'permissions.user.view.dashboard' },
{ Admin: 'permissions.admin.view.dashboard' },
{ Admin: 'permissions.user.view.dashboard' }
]
const roleData = {
'User.permissions.user.view.dashboard': true,
'Admin.permissions.user.view.dashboard': true,
'Admin.permissions.admin.view.dashboard': true,
};
const mappedData = Object.keys(roleData).reduce((acc, entry) => {
const dotIndex = entry.indexOf('.');
const parts = [entry.slice(0,dotIndex), entry.slice(dotIndex+1)];
acc[parts[0]] ??= [];
acc[parts[0]].push(parts[1]);
return acc;
}, {});
console.log(mappedData);
You can use reduce function:
const result = permissions.reduce((acc, cur) => {
const key = Object.keys(cur)[0]
if (acc[key]) {
acc[key] = [...acc[key], cur[key]]
} else {
acc[key] = [cur[key]]
}
return acc
}, {})

How to update an array inside an array of objects with useState function component

How to update array inside array use state function component
the array is inside the setTask() and how to use setTask() and add new_photo in photos
const new_photo = "testing2.png"; <------------ insert this value in photos
const [task, setTask] = useState([
{
task_id: 123,
title: "sample",
photos: ["testing1.png"] <------------ here
}
])
Result should be like this:
[
{
task_id: 123,
title: "sample",
photos: ["testing1.png","testing2.png"]
}
]
setTask((oldTasks) => {
const myTask = oldTasks.find(t => t.task_id === 123)
return [
...oldTasks.filter(ot => ot.task_id != 123),
{
...myTask,
photos: [...myTask.photos, new_photo],
}
]
})
Ref: spread operator and asynchronous state update
const new_photo = "testing2.png"; // <------------ insert this value in photos
const [task, setTask] = useState([
{
task_id: 123,
title: "sample",
photos: ["testing1.png"] // <------------ here
}
])
const arrayPush = (ID) => {
setTask((element) => {
element.forEach((e) => {
if (e.task_id === ID) {
e.photos.push(new_photo);
}
});
});
console.log(task);
}
const arrayPush2 = (ID) => {
let taskCopy = Array.from(task)
taskCopy.forEach((element) => {
if (element.task_id === ID) {
element.photos.push(new_photo);
}
});
setTask(taskCopy)
};
arrayPush(123)
arrayPush2(123)

How to turn array of objects in unique array?

This is my initial data:
const data = [
{
"user":{
"amount":"1",
"date":"2020-07-31T18:34:48.635Z",
"shiftSelected":"10:00"
}
},
{
"user":{
"name":"Name",
"surname":"aaa",
"obs":"eee"
}
}
]
I'm trying to turn an array of objects in unique array. This is my output:
const newData = {
amount: "1",
date: "2020-07-31T18:34:48.635Z",
shiftSelected: "10:00",
name: "Name",
surname:"aaa",
obs:"eee"
}
I can iterate over the array with a map call: let b = data.map(item => item.user), but I need to write more code to join then. I know that it's possible to do it with one unique logic. I tried but without successful.
You can use reduce with Object.assign to merge the properties of the objects. Note that with this method, later properties will overwrite previous ones.
const data = [
{
"user":{
"amount":"1",
"date":"2020-07-31T18:34:48.635Z",
"shiftSelected":"10:00"
}
},
{
"user":{
"name":"Name",
"surname":"aaa",
"obs":"eee"
}
}
];
const result = data.reduce((acc,{user})=>Object.assign(acc,user), {});
console.log(result);
Object spread syntax will also work.
const data = [
{
"user":{
"amount":"1",
"date":"2020-07-31T18:34:48.635Z",
"shiftSelected":"10:00"
}
},
{
"user":{
"name":"Name",
"surname":"aaa",
"obs":"eee"
}
}
];
const result = data.reduce((acc,{user})=>({...acc, ...user}), {});
console.log(result);
const data = [
{
"user":{
"amount":"1",
"date":"2020-07-31T18:34:48.635Z",
"shiftSelected":"10:00"
}
},
{
"user":{
"name":"Name",
"surname":"aaa",
"obs":"eee"
}
}
]
let b = data.reduce((acc, rec) => {
const { user } = rec
return { ...acc, ...user}
}, {} )
console.log(b)
use this after map over datas this is not a logic but.....
const x = data.map(obj => obj.user);
function flatObj(arr) {
const res = {};
arr.forEach(y => {
for(const key in y) {
res[key] = y[key];
}
})
return res;
}
const resault = flatObj(x);

Async-await 'Promise {<pending>} with Array.prototype.map

I know there are many questions discuss about the same error and I saw most of them and they didn't fix my problem.
I wrote this code:
const userOrganizationGroups = (organizationGroupsList) => {
if (Array.isArray(organizationGroupsList) && organizationGroupsList.length) {
const result = organizationGroupsList.map(async (element) => {
const { organizationId, groupId } = element;
const { Organizations, Groups } = models;
const organization = await Organizations.findOne(
{ _id: organizationId },
{ name: 1, _id: 0 },
);
const group = await Groups.findOne({ _id: groupId });
return Object.assign({}, {
organizationName: organization.name,
group: group.name,
});
});
return result;
}
};
when I debug the code:
console.log('userOrganizationGroups : ',userOrganizationGroups(list))
I got such a result:
userOrganizationGroups: Promise { <pending> }
I found a similair question: Promise { } - Trying to await for .map and I used the solution mentioned in the question:
const userOrganizationGroups = async (organizationGroupsList) => {
if (Array.isArray(organizationGroupsList) && organizationGroupsList.length) {
const result = await Promise.all(organizationGroupsList.map(async (element) => {
const { organizationId, groupId } = element;
const { Organizations, Groups } = models;
const organization = await Organizations.findOne(
{ _id: organizationId },
{ name: 1, _id: 0 },
);
const group = await Groups.findOne({ _id: groupId });
return Object.assign({}, {
organizationName: organization.name,
group: group.name,
});
}));
return result;
}
How can I fix this issue?
instead of
console.log('userOrganizationGroups : ',userOrganizationGroups(list))
use
userOrganizationGroups(list).then( groups => console.log('userOrganizationGroups : ', groups)
or
(async () => {
const groups = await userOrganizationGroups(list);
console.log('userOrganizationGroups : ', groups);
})();
console.log() was called first because you didn't wait using await or then.
So You should write below instead of
console.log('userOrganizationGroups : ',userOrganizationGroups(list))
;(async () => {
const resultGroups = await userOrganizationGroups(list)
resultGroups.forEach(group => {
console.log(`group: ${JSON.stringfy(group, null, ' ')}`)
})
})()

Categories

Resources