Promise not getting resolved inside reduce array method javascript - javascript

I have large array of objects and filtered the objects based on the userID. Here is the code below.
const filteredArr = LargeArr.Items.reduce(
async(acc, { attributes: { dob, name, picture} = { dob: null, name: null, picture: null }, userID }) => {
let pic = null;
if (picture) { pic = await getPic(picture); } // async here
acc[userID] = { name, userID, pic, dob };
return acc;
}, {});
Expected Output :
{
'1595232114269': {
name: 'Mark Status',
userID: '1595232114269',
picture: 'mark-status.jpg',
dob: '2020-08-10'
},
'48e69555d778f9b9a3a1d553b9c3b8f7dd6a3394ac82df1433b60a69c055d23d': {
name: 'Jack Thomas',
userID: '48e69555d778f9b9a3a1d553b9c3b8f7dd6a3394ac82df1433b60a69c055d23d',
picture: 'jack-thomas.jpg',
dob: '1990-12-20'
},
'48e69555d778f9b9a3a1d553b9c3b8f7dd6a3394ac82df1433b60a69c055d47p': {
name: 'Petro Huge',
userID: '48e69555d778f9b9a3a1d553b9c3b8f7dd6a3394ac82df1433b60a69c055d47p',
picture: 'petro huge.jpg',
dob: '1856-12-20'
},
'48e69555d778f9b9a3a1d553b9c3b8f7dd6a3394ac82df1433b60a69c055d55j': {
name: 'Mark Henry',
userID: '48e69555d778f9b9a3a1d553b9c3b8f7dd6a3394ac82df1433b60a69c055d55j',
picture: 'mark-henry.jpg',
dob: '2005-12-29'
}
}
I need to get picture from an api which is asynchronous, so used async await inside the reduce method. The problem here is it is always showing as Promise pending. If this was an array of object, then i can return Promise.all, but since this is object containing object how can i proceed with this inside reduce method? I need the exact same expected output.
Can somebody help me with this? Any help would be really appreciated.

To use reduce while iterating over items asynchronously, you'd have to have the accumulator which gets passed from callback to callback to be a Promise. While this is possible, it'll make things pretty difficult to read, and introduces some unnecessary syntax noise.
Use a plain for loop instead:
const filteredArr = {};
for (const item of LargeArr.Items) {
const { attributes: { dob, name, picture} = { dob: null, name: null, picture: null } } = item;
const pic = picture ? await getPic(picture) : null;
filteredArr[userID] = { name, uesrID, pic, dob };
}
If you really wanted to take the reduce route:
LargeArr.Items.reduce(
(acc, { attributes: { dob, name, picture} = { dob: null, name: null, picture: null }, userID }) => {
return acc.then(async (acc) => {
let pic = null;
if (picture) { pic = await getPic(picture); } // async here
acc[userID] = { name, userID, pic, dob };
return acc;
});
}, Promise.resolve({})
)
.then((filteredArr) => {
// do stuff with filteredArr
});
Unless the getPic calls need to be made in serial, you could consider using Promise.all instead, to iterate through the whole array at once, rather than waiting on the resolution of the prior Promise before going onto the next.
If your API can handle Promise.all:
const filteredArr = {};
await Promise.all(LargeArr.Items.map(async (item) => {
const { attributes: { dob, name, picture} = { dob: null, name: null, picture: null } } = item;
const pic = picture ? await getPic(picture) : null;
filteredArr[userID] = { name, uesrID, pic, dob };
}));

Related

How to parse a property of a react object

I am working in react and have a resonse ( ReviewerService.getReviewers()) that returns an array of values:
0: {id: 1, firstName: 'John', lastName: 'Doe', email: 'johndoe#aol.com', responses: '{"q1":"yes","q2":"no","q3":"yes","rating":4}'}
1: {id: 2, firstName: 'bob', lastName: 'jefferson', email: 'bob#aol.com', responses: '{"q1":"bob","q2":"yes","q3":"yes","rating":5}'}.
If this.state = { reviewers: [] }.
How do i pass the response data into reviewers and parse the responses property at the same time? Therefore, then I can access these properties of the responses easily.
class ListReviewsComponent extends Component {
constructor(props) {
super(props);
this.state = {
reviewers: [],
};
}
async componentDidMount() {
await ReviewerService.getReviewers().then((res) => {
this.setState({ reviewers: res.data });
});
this.setState({ reviewers.responses: JSON.parse(this.state.reviewers.responses)}); // error
}
can this work
async componentDidMount() {
try {
const res = await ReviewerService.getReviewers();
// got the data
const reviewers = res.data;
// not parse the responses for each reviewer
const mappedReviewers = reviewers?.map(reviewer => {
try {
const parsedResponses = JSON.parse(reviewer.responses)
// do you need to convert parsedResponses to an array ??
return {
...reviewer,
responses: parsedResponses
}
} catch(error) {
return {
...reviewer,
responses: [] //
}
}
});
this.setState({ reviewers: mappedReviewers})
} catch (error) {
// log errors
}
}
Hope this helps you to sort out the issue
I think that you array returned by
ReviewerService.getReviewers()
should be in json format, treated or parsed, before, setted in setState:
data = [ {id: 1, firstName: 'John', lastName: 'Doe', email: 'johndoe#aol.com', responses: '{"q1":"yes","q2":"no","q3":"yes","rating":4}'}
,{id: 2, firstName: 'bob', lastName: 'jefferson', email: 'bob#aol.com', responses: '{"q1":"bob","q2":"yes","q3":"yes","rating":5}'} ];
Then you do this, putting array in a json treated object format
async componentDidMount() {
await ReviewerService.getReviewers().then((res) => {
this.setState({ reviewers: res.data });
});
When you do:
this.setState({ reviewers: res.data });
You area putting on this.state.reviewers, all list and all objects nested in.
You could access this.state on this component like this method below:
getResponsesOfReviewersOnArrayByIndex = (index) => {
return this.state.reviewers[index].responses
}
Or just in some method access,
this.state.reviewers[i].firstName
You can try this to understand better the JSON parse function:
const reviewersData = '[{"name":"neymar"}, {"name":"junior"}]';
const reviewers = JSON.parse(reviewersData);
console.log(reviewers[0].name);
In this W3Schools to see more examples of JSON.parse()
Hope this helps.

Storing the name of the current user and matched user in state using useEffect returning incorrect output

I have a function in my context that takes in two data points, the current username and the username of a randomly selected user who is online
const pairProgrammingMatching = (username, matchedUser) => {
setPairUsers({username: matchedUser })
}
I call this function in another component using useEffect()
useEffect((() => {
console.log('curr username is', username)
pairProgrammingMatching(username, checkOnlineUsers(pickRandom()).username)
console.log('inside pairUsers state', pairUsers)
}), [])
checkOnlineUsers simply returns a list of all users who are currently online and active
let checkOnlineUsers = () => {
const results = onlineUsers.filter(obj => {
return obj.profile.is_online === true && obj.profile.currently_active === false && obj.username !== user.username
})
return results
}
Here's a sample of what this function returns
[{ email: "***#gmail.com"
first_name: ""
last_name: ""
profile: {city: 'Washington DC', country: 'USA', is_online: true, currently_active: false}
username: "testingUser" }]
pickRandom simply picks a number between 0 and n, with n being the number of users returned in checkOnlineUsers:
const pickRandom = () => {
return Math.round(Math.random()*onlineUsers.length)
}
The problem here is that if I print out pairUsers inside my useEffect I expect to see this:
{ 'userA': 'testingUser' }
with userA being the name of the current user. However this is what I get:
{username: undefined}

Create new object from array

I'm trying to create new object with different properties name from Array.
Array is:
profiles: Array(1)
0:
column:
name: "profileName"
title: "Profile name"
status: "Active"
I want to create new function that return object with two properties:
id: 'profileName',
profileStatus: 'Active'
The function that I have create is returning only one property as undefined undefined=undefined.
function getProfile(profiles) {
if (!profiles.length) return undefined;
return profiles.reduce((obj, profile) => {
console.log('profiles', profile);
return ({
...obj,
id: profile.column.name,
profileStatus: profile.status,
});
}, {});
}
The function getProfile is taking as input array 'profiles' from outside,
I've just tested here and this seems to be working actually
const getProfile1 = (p) => p.reduce((obj, profile) =>({
...obj,
id: profile.column.name,
profileStatus: profile.status,
}), {});
You can use map as an alternative.
var profiles = [{"column":{"name": "profileName3","title": "3Profile name"},"status": "Active"},{"column":{"name": "profileName","title": "Profile name"},"status": "Active"}];
function getProfile(profiles) {
if (!profiles.length) return undefined;
return profiles.map(function(profile,v){
return {id:profile.column.name,profileStatus: profile.status};
});
}
console.log(getProfile(profiles));
Whenever I use reduce in this way, I usually index the final object by some sort of an id. As noted in another answer, you could use map in this situation as well. If you really want your final data structure to be an object, however, you could do something like this:
/**
* returns object indexed by profile id
*/
const formatProfiles = (profiles) => {
return profiles.reduce((obj, profile) => {
return {
...obj,
[profile.id]: {
id: profile.column.name,
profileStatus: profile.status,
}
};
}, {});
};
const profiles = [
{
id: 0,
status: 'active',
column: {
name: "profile_name_1",
title: "profile_title_1",
},
},
{
id: 1,
status: 'inactive',
column: {
name: "profile_name_2",
title: "profile_title_2",
}
}
];
const result = formatProfiles(profiles);
/**
* Result would look like this:
*/
// {
// '0': { id: 'profile_name_1', profileStatus: 'active' },
// '1': { id: 'profile_name_2', profileStatus: 'inactive' }
// }

How can I pass parameter correctly?

const root = {
user: (id) => {
console.log("returning object " + JSON.stringify(id.id) + " " + JSON.stringify(storage.select("users", id.id)))
return storage.select("users", id.id)
}
}
I want to call the arrow function in root.user but I think I can't pass the parameter correctly, so I tried this --> let user = root.user('101')
and on the console I got this -->
returning object undefined
[{"firstName":"Gokhan","lastName":"Coskun","login":"gcoskun","id":101}]
{"firstName":"George","lastName":"Clooney","login":"gclooney"}
[{"firstName":"Gokhan","lastName":"Coskun","login":"gcoskun","id":101}]
I wanted the user with the id 101 get returned and got instead all of the users returned.
Why are you doing id.id but passing a string? You either pass an object with an id prop (root.user({ id: '101' })) or replace id.id with simply id.
Also, it looks like the id fields in your user objects are of type number, while you are passing a string, so depending on the logic inside storage.select you might have to change that.
Passing a number id:
// Just mocking it for the example:
const storage = {
select(key, id) {
return [
{ firstName: 'Gokhan', lastName: 'Coskun', login: 'gcoskun', id: 101 },
{ firstName: 'George', lastName: 'Clooney', login: 'gclooney' },
{ firstName: 'Gokhan', lastName: 'Coskun', login: 'gcoskun', id: 101 },
// Depending on the logic here, these types need to match.
// Using == instead of === so that it's not required here.
].filter(user => user.id == id)
},
};
const root = {
user: (id) => {
console.log(`ID = ${ id }`);
// We make sure we only return a single user or null if there isn't one:
return storage.select('users', id)[0] || null;
},
};
const user = root.user('101');
console.log(user);
Passing an object with an id prop of type number:
// Just mocking it for the example:
const storage = {
select(key, id) {
return [
{ firstName: 'Gokhan', lastName: 'Coskun', login: 'gcoskun', id: 101 },
{ firstName: 'George', lastName: 'Clooney', login: 'gclooney' },
{ firstName: 'Gokhan', lastName: 'Coskun', login: 'gcoskun', id: 101 },
// Depending on the logic here, these types need to match.
// Using == instead of === so that it's not required here.
].filter(user => user.id == id);
},
};
const root = {
user: (query) => {
console.log(`ID = ${ query.id }`);
// We make sure we only return a single user or null if there isn't one:
return storage.select('users', query.id)[0] || null;
},
};
const user = root.user({ id: '101' });
console.log(user);

How do i destructure an object

i have a function
const displayUserPhotoAndName = (data) => {
if (!data) return;
// add your code here
clearNotice();
};
After the first if(!data) return; statement that terminates the function if the expected data parameter is not provided, create a statement that de-structures the data parameter and obtains the results property from it;
Create a second statement in the next line that de-structures the results variable you just created, and obtain the first item from it (it is an Array! See https://randomuser.me/api/). Your de-structured array item should be declared as profile. This represents the profile data for the user gotten from the API call that you want to display in your app.
const displayUserPhotoAndName = (data) => {
if(!data) return;
// add your code here
const {results: results} = data;
const {profile: results} = results;
this is where i am right now but i still get an error message saying "you have not destructured the profile property from results obtained from data passed to displayUserPhotoAndName function. your assistance will be very much appreciated...
You can do this two ways:
Your approach of two steps:
const {results} = data;
const {profile} = results;
Or in one step:
const {results: {profile}} = data;
For a better understanding you should check out the documentation of object destructuring.
You can do as following if you need nth element from results array use
{ results: { n: profile } } = data;
let data = { results: [1, 2, 3, 4] }
let { results: { 0: profile1, 2: profile2 } } = data;
console.log(profile1)
console.log(profile2)
Even you can do further destructuring
let data = { results: [{ name: 'myname1', gender: 'male' }, { name: 'myname2', gender: 'male' }, { name: 'myname3', gender: 'female' }, { name: 'myname4', gender: 'male' }] }
let { results: { 0: profile1, 2: { name, gender } } } = data;
console.log(profile1)
console.log(name)
console.log(gender)
Here an example:
const data = {
results: {
name: "test1",
surname: "123"
},
profile: {
name: "test2",
surname: "321"
}
};
const { results, profile } = data;
console.log(results);
console.log("====");
console.log(profile);

Categories

Resources