Array is empty after a foreach loop (async/await) - javascript

I'm trying to retrieve an array of cards for a project. However, in my function, the final contacts array returns an empty array.
I know that, because I have an async call to another funcion inside the forEach loop, the loop doesn't execute as intended. However, I'm very newbie when it comes to deal with this issues, so I want to ask you what's the best approach to deal with this.
This is my code:
export const extractsIDSForUser = async (currentUser: User) : Promise <Object> => {
let contactCards = currentUser.contacts;
const contacts = [];
const usersRef = await firebase.firestore().collection('Users').get();
const usersSnapshot = usersRef.docs.map(doc => doc.data());
contactCards.forEach(async folder => {
const ids = [];
folder.forEach(contact => {
ids.push(contact);
});
for (let i = 0; i < ids.length; i +=1) {
const contact = ids[i];
for (let j = 0; j < usersSnapshot.length; j += 1) {
const userId = usersSnapshot[j].id;
// Async call to function
const cardsFromUser = await extractCardsFromUser(userId);
const arrayCards = Object.values(cardsFromUser);
if (arrayCards.length > 0) {
for (let j = 0; j < arrayCards.length; j += 1) {
const arrayId = arrayCards[j].id;
const sameCardId = arrayId === contact;
if (sameCardId) {
// Where I insert the values into the array
contacts.push(arrayCards[j]);
}
}
}
}
}
});
// this is empty
return contacts;
}
What will be the best approach to deal with this?

I think you have already found a solution, but I had a similar problem and found this article quite helpful.
You could use a traditional for (const contactCard of contactCards) and it will work, but it will be less efficient than using a Promise.all approach.

Related

How to check value of innerText if is null

I am developing a web scrapping and I am trying to solve latest problems with this.
In this case I need to check if innerText is null inside loop.
for(var j = 0; j<linkPlayersNew.length;j++){
var stats = [];
for (let i = 2; i <= 23; i++) {
const values = await page.evaluate((nth,nth2) => {
const test= parseInt(nth);
const test2= parseInt(nth2);
return document.querySelector('div.ui-table__row:nth-child('+test2+') > div:nth-child('+test+')').innerText;
},i,j+1);
stats.push(values);
}
data.push(stats);
}
console.log(data);
This works correct but I need to check if innerText is not null inside loop.

Javascript split strings in array on specific index

I have this array of strings.
const numbersArray = ['1000','10000','100000']
My goal is to split each one of them on specific index for example: output of 1000 should be 1,000 and etc...
Here is what i have right now:
const splitArrayHandler = (arr) =>{
for (let i = 0; i < arr.length; i++) {
let indexOfSymbol = Math.round(arr[i].length / 3)
return splitAtIndex(arr[i],indexOfSymbol)
}
}
const splitAtIndex = (value,index) => {
return value.substring(0,index) + ',' + value.substring(index)
}
splitArrayHandler(numbersArray)
The first function splitArrayHandler loops through my array,finds specific index of the symbol in the string and then function splitAtIndex does the rest of the hard work.
The problem is only first element of the string is passing to the splitAtIndexfunction and I dont understand why. any suggestions please?
const numbersArray = ['1000','10000','100000']
const splitArrayHandler = (arr) =>{
for (let i = 0; i < arr.length; i++) {
let indexOfSymbol = Math.round(arr[i].length / 3)
return splitAtIndex(arr[i],indexOfSymbol)
}
}
const splitAtIndex = (value,index) => {
return value.substring(0,index) + ',' + value.substring(index)
}
splitArrayHandler(numbersArray)
Use Intl.NumberFormat for the job. No need for string parsing / manipulating:
const numbersArray = ['1000', '10000', '100000', '654654686156', '1000.66', '10e14', '0xFFFF'];
const format = new Intl.NumberFormat('en-US').format;
const formattedNumbers = numbersArray.map(Number).map(format);
console.log(formattedNumbers);
You are breaking the loop by returning the splitAtIndex function. Create another array and push the results to it.
const splitArrayHandler = (arr) =>{
let arr2 = []
for (let i = 0; i < arr.length; i++) {
let indexOfSymbol = Math.round(arr[i].length / 3)
arr2.push(splitAtIndex(arr[i],indexOfSymbol))
}
return arr2
}
You might use regular expression and map function (though there is no real difference between map and hard coded loop)
const numbersArray = ['1000','10000','100000']
function addComa(x) {
return x.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
}
const resolved = numbersArray.map(addComma)
console.log(resolved) // ['1,000','10,000','100,000']

Running forEach on Object.entries does not return the same thing as a for loop

I am iterating over an object using a regular for loop and that works fine for me. But, I was trying to remove all for loops of my code in favor of array iteration instead and, for some reason I can't understand why when using forEach I get a different result.
Note: forEach here is from a module called p-iteration
https://www.npmjs.com/package/p-iteration
This works fine, it returns the correct values.
for await (const [key, value] of Object.entries(tatGroupedByRegion)) {
onTarget = 0;
notOnTarget = 0;
const cases = [];
await forEach(value, async email => {
if (!cases.includes(email.new_name)) {
cases.push(email.new_name);
isOnTarget(email);
}
});
backlogData[key].tatd1 = percentage(onTarget, notOnTarget);
tatd1Total.value += parseInt(percentage(onTarget, notOnTarget), 10);
if ((parseInt(percentage(onTarget, notOnTarget) !== 0), 10)) {
tatd1Total.count += 1;
}
}
This does not work,this part here backlogData[key].tatd1 = percentage(onTarget, notOnTarget), returns the same value over and over.
await forEach(Object.entries(tatGroupedByRegion), async ([key, value]) => {
onTarget = 0;
notOnTarget = 0;
const cases = [];
await forEach(value, async email => {
if (!cases.includes(email.new_name)) {
cases.push(email.new_name);
isOnTarget(email);
}
});
backlogData[key].tatd1 = percentage(onTarget, notOnTarget);
tatd1Total.value += parseInt(percentage(onTarget, notOnTarget), 10);
if ((parseInt(percentage(onTarget, notOnTarget) !== 0), 10)) {
tatd1Total.count += 1;
}
});
exports.forEach = async (array, callback, thisArg) => {
const promiseArray = [];
for (let i = 0; i < array.length; i++) {
if (i in array) {
const p = Promise.resolve(array[i]).then((currentValue) => {
return callback.call(thisArg || this, currentValue, i, array);
});
promiseArray.push(p);
}
}
await Promise.all(promiseArray);
};
This is the implementation of forEach that you're using. The callback receives this as the first argument, this can be a problem.

Convert generator to normal function

I am working on a project and there is some refactor to do. For internal decision we do not want to use generators and I came across this code (which it looks weird to me because it seems that there is no need for a generator at all). How would I go to convert it to a normal function (I don't think there is any async operation as far as I can tell)?
Just to make clear I do not want to use generators in this code.
Code:
const getResults = (totalData) => function* getNext() {
const combinations = totalData.reduce((a, b) => a * b.length, 1)
for (let i = 0; i < combinations; i++) {
yield createSolution(i, totalData)
}
return null
}
This is how is being called:
const result = getResults(obj.elementsInObj);
for (let data of result()) {
const resolve = validateData(data, obj.elementsInObj)
if (resolve) {
return resolve
}
}
Well, you can remove the asterisk and yield operator and create an internal array to store the solutions, then you can return that array and loop over it.
const getResults = (totalData) => {
const combinations = totalData.reduce((a, b) => a * b.length, 1),
arr = [];
for (let i = 0; i < combinations; i++) arr.push(createSolution(i, totalData));
return arr;
}
const results = getResults(obj.elementsInObj);
for (let data of results) {
const resolve = validateData(data, obj.elementsInObj)
if (resolve) return resolve
}

How to do deep copy of Excel range values in Office-JS?

It seems that the Excel JS-API does shallow copies of Range.values. If I want to read a range from one place and write differently modified copies of it to 2 different places I need to use a deep copy of the range: how do I do that?
This code still does a shallow copy:
async function setValue() {
try {
await Excel.run(async (context) => {
let sheet = context.workbook.worksheets.getActiveWorksheet();
// A1 contains 9876543210
let rng1 = sheet.getRange("a1").load("values");
await context.sync();
console.log(JSON.stringify(rng1.values));
// rng1 value is correct =9876543210
let rng2 = sheet.getRange("B4");
let rng3 = sheet.getRange("B6");
let avar = [[]];
for (var j = 0; j < rng1.values.length; j++) {
for (var k = 0; k < rng1.values[0].length; k++) {
avar[j][k] = rng1.values[j][k];
}
}
rng3.values = avar;
console.log(JSON.stringify(avar));
// rng2 value is correct =9876543210
let avar2 = avar[0][0];
rng3.values[0][0] =avar2 + 0.01;
console.log(JSON.stringify(rng3.values));
// rng3 value is correct =9876543210.01
rng2.values = avar;
console.log("Show values of the 3 ranges before Sync")
console.log(JSON.stringify(rng1.values));
console.log(JSON.stringify(rng2.values));
console.log(JSON.stringify(rng3.values));
// rng2 and rng3 both show as 987654321.01
// BUT ONLY Rng3 has been altered!
await context.sync();
});
console.log("Done!");
}
catch (error) {
OfficeHelpers.Utilities.log(error);
}
}
To answer my own question - you can make a deep copy using JQuery extend
let avar = [];
$.extend(true, avar, rng1.values);
rng3.values = avar;

Categories

Resources