Cannot push into array from inside forEach loop - javascript

Whenever I try to push into the pathItem array, the response is an empty array. What is happening?
const categories = await Cars.find().distinct("bodytype");
let pathItem = [];
categories.forEach(async (element) => {
const countQuery = await Cars.countDocuments({
bodytype: element,
});
const numberOfPages = Math.ceil(countQuery / 12);
for(let count=1; count<=numberOfPages; count++) {
const carPath = {paths: { cartype: element, pages: count }}
pathItem.push(carPath)
}
});
console.log(pathItem) // this returns an empty array

This is what worked:
const categories = await Cars.find().distinct("bodytype");
let pathItem = [];
for(let elements of categories) {
const countQuery = await Cars.countDocuments({
bodytype: element,
});
const numberOfPages = Math.ceil(countQuery / 12);
for(let count=1; count<=numberOfPages; count++) {
const carPath = {paths: { cartype: element, pages: count }}
pathItem.push(carPath)
}
});
console.log(pathItem) // this does not returns an empty array anymore
As Tushar mentioned, forEach does not wait for async calls, so Mongoose requests were not fulfilled before the forEach exited each loop.

Related

iterating Javascript array and delete based on condition

I want to iterate through an array of words, look up the definition and delete the word if no definition is found.
my code looks as follows;
var words = ["word1", "word2", "word3",]
function Meaning(words){
const getMeaning = async () => {
const response = await fetch(`https://api.dictionaryapi.dev/api/v2/entries/en/${words}`)
const myJson = await response.json()
for(i = 0; i < words.length; ++i) {
if(!response[i]){
myJson.splice(i,1)
console.log(myJson)
}
}}
This is not really doing anything atm. Where am I going wrong?
edit to add context
tried like this as well;
for(i = 0; i < words.length; ++i)
fetch(`https://api.dictionaryapi.dev/api/v2/entries/en/${words[i]}`).then((response) => {
if (response === 404) {
let response = words
words[i].splice(i,1)
console.log(response)
}
throw new Error('Something went wrong');
})
.then((responseJson) => {
let response = words
response[i].splice(i,1)
})
.catch((error) => {
console.log(error)
});
I can print out the 404 error when it finds no definition, but I can't remove it from the words array
After quick look at the API, and it appears to handle only single words, so the caller needs to make the requests one at a time. Here's how to do it...
const baseUrl = 'https://api.dictionaryapi.dev/api/v2/entries/en/';
// one word lookup. resolve to an array of definitions
async function lookupWord(word) {
const res = await fetch(baseUrl + word);
return res.json();
}
// resolve to a bool, true if the word is in the corpus
async function spellCheck(word) {
const defArray = await lookupWord(word);
return Array.isArray(defArray) && defArray.length > 0;
}
// create a spellCheck promise for every word and resolve with the results
// note, this mutates the array and resolves to undefined
async function spellCheckWords(array) {
const checks = await Promise.all(array.map(spellCheck));
for (let i=array.length-1; i>=0; i--) {
if (!checks[i]) array.splice(i,1);
}
}
// test it (a little)
let array = ['hello', 'whereforeartthou', 'coffee'];
spellCheckWords(array).then(() => {
console.log(array)
})
try this code, you need to check every single element of array from response
var words = ["word1", "word2", "word3"];
function Meaning(words) {
const getMeaning = async () => {
const response = await fetch(`https://api.dictionaryapi.dev/api/v2/entries/en/${words}`)
const myJson = await response.json()
let result = [];
myJson.forEach(element => {
if(words.includes(element)) {
result.push(element)
}
});
return result;
}
return getMeaning();
}

JSON.stringify won't work on JSON Object where properties are created with Object.defineproperty

I'm currently writing a web scraping discord bot and want to implement the logic. I create the final Object it also has all properties that i set but when i try to console.log(totalData) it returns {}
same result when i do console.log(JSON.stringify(totalData)) => {}
i have also tried it with console.dir(), console.table()
The code:
const puppeteer = require('puppeteer')
let url = "https://www.worldometers.info/coronavirus/";
async function scrapeAllByCountry(page, country){
const totalData = {}
let endPosition;
// XPath to the data
allData = ["/html/body/div[3]/div[3]/div/div[6]/div[1]/div/table/tbody[1]"]
headerData = ["Country", "Total Cases", "New Cases", "Total Deaths", "New Deaths", "Total Recovered", "New recovered", "Active Cases", "Serious, Critical", "Total Cases/1M pop", "Deaths/1M pop", "Total Tests", "Tests/1M pop", "Population"]
// scraping the data
var element = await page.waitForXPath(allData[0])
let allTableData = await page.evaluate(element => element.textContent, element)
allTableData = allTableData.toLowerCase()
// replacing comma with nothing so numbers return false when i call isNaN()
allTableData = allTableData.replaceAll(",", "")
// searches for the country in the string and saves it into positionCountry
let positionCountry = allTableData.search(country.toLowerCase())
// Adds 150 more chars so the full Data is in it
let data = allTableData.substring(positionCountry, positionCountry + 150)
// creates array for each row
tempDataArray = data.split("\n");
// searches for the next NAN in the array and sets the end position
for(let i = 0; i < tempDataArray.length; i++){
// if the current element is a number do nothing if it is a NaN set endPosition and exit
if(isNaN(tempDataArray[i]) & i != 0){
endPosition = i
break;
}
}
// create new Array with endposition
let filteredArray = [];
for(let i = 0; i < endPosition; i++){
filteredArray.push(tempDataArray[i])
}
// Merge both Arrays into Object
for(let i = 0; i < filteredArray.length; i++){
// sets the header with a value on the totalDataObject
Object.defineProperty(totalData, headerData[i], {
value: filteredArray[i],
writable: false
})
// here it confirms that it has all Properties
console.log(totalData.hasOwnProperty(headerData[i]))
}
console.log(JSON.stringify(totalData)) // outputs {}
return totalData
}
async function main(){
const browser = await puppeteer.launch({})
const page = await browser.newPage()
await page.goto(url)
let result = await scrapeAllByCountry(page, "Germany")
console.log(JSON.stringify(result)) // also returns {}
console.log(result.hasOwnProperty("Country"))
browser.close()
}
main();

How to refactor a series of functions in a promise to a loop

How do I refactor this code using a for loop:
const [userA, userB, userC, userD] = await Promise.all([
createTestUser(),
createTestUser(),
createTestUser(),
createTestUser()
]);
You can do
const userPromises = [];
for (let i=0; i<4; i++) {
userPromises.push(createTestUser());
}
const users = await Promise.all(userPromises);
// const [userA, userB, userC, userD] = users;
Or, a bit shorter:
const users = await Promise.all(Array.from({length: 4}, _ => createTestUser()));

Recursive function to ensure the return length is 5

I'm fetching some data from an xml source, but I need to check that the length of the array (return value) is 5, sometimes the response serves data with less than 5 elements (it's random).
If the return value (colorArray) is 5, the promise resolves with the correct array. Otherwise, if the function re-runs the promise resolves with undefined.
Appreciate any help in understanding why I'm getting undefined when colorArray.length is less than 5, or if anyone has any better suggestions about how I should run the code.
Thanks.
const runAxios = async () => {
console.log("function");
const res = await axios.get("/api/palettes/random");
let parser = new DOMParser();
let xml = parser.parseFromString(res.data, "application/xml");
let colors = xml.getElementsByTagName("hex");
const colorArray = [];
for (let i = 0; i < colors.length; i++) {
let colorList = colors[i].firstChild.nodeValue;
colorArray.push(colorList);
}
if (colorArray.length === 5) return colorArray;
else runAxios();
};
const result = runAxios();
result.then(e => {
console.log(e);
});
The problem is that you never returned runAxios:
const runAxios = async () => {
console.log("function");
const res = await axios.get("/api/palettes/random");
let parser = new DOMParser();
let xml = parser.parseFromString(res.data, "application/xml");
let colors = xml.getElementsByTagName("hex");
const colorArray = [];
for (let i = 0; i < colors.length; i++) {
let colorList = colors[i].firstChild.nodeValue;
colorArray.push(colorList);
}
if (colorArray.length === 5) return colorArray;
else return runAxios(); // <----------------------------------This
};
const result = runAxios();
result.then(e => {
console.log(e);
});
Also, depending on your requirements, I would suggest a do-while loop:
const runAxios = async () => {
do {
console.log("function");
const res = await axios.get("/api/palettes/random");
let parser = new DOMParser();
let xml = parser.parseFromString(res.data, "application/xml");
let colors = xml.getElementsByTagName("hex");
const colorArray = [];
for (let i = 0; i < colors.length; i++) {
let colorList = colors[i].firstChild.nodeValue;
colorArray.push(colorList);
}
} while(colorArray.length != 5);
return colorArray;
};
const result = runAxios();
result.then(e => {
console.log(e);
});

Why do the last two functions work, but not the first?

I have three different functions that should do the same thing, populate an array with resolved Promises, but it's not working for the first example.
Here's my code:
(async() => {
const items = [];
const someFn = async() => {
const v = await Promise.resolve(10);
items.push(Math.random());
return Promise.resolve(v * 10);
}
const arr = [];
for (let i = 0; i < 10; i++) {
arr.push(someFn);
}
await Promise.all(arr);
console.log("item 1", items);
})();
(async() => {
const items = [];
const someFn = async() => {
const v = await Promise.resolve(10);
items.push(Math.random());
return Promise.resolve(v * 10);
}
const arr = [...Array(10).keys()].map(someFn)
await Promise.all(arr);
console.log("items 2", items);
})();
(async() => {
const items = [];
const someFn = async() => {
const v = await Promise.resolve(10);
items.push(Math.random());
return Promise.resolve(v * 10);
}
for (let i = 0; i < 10; i++) {
await someFn();
}
console.log("items 3", items);
})()
This is the output:
item 1 []
items 2 [ 0.7450904427103939,
0.37106667256699555,
0.12035280341441346,
0.265221052932904,
0.7775494303685422,
0.4872532010723445,
0.6497680191919464,
0.2570485072009576,
0.5613137531648884,
0.95109416178435 ]
items 3 [ 0.25328649499657585,
0.5452758396760038,
0.7274346878509064,
0.9306670111476503,
0.22942578229725785,
0.32547900377461625,
0.9722902638678983,
0.9964743517593542,
0.2828162584401659,
0.7672256760378469 ]
Notice how item 1 is an empty array.
That's because in the first example, someFn is never executed:
for (let i = 0; i < 10; i++) {
arr.push(someFn);
}
await Promise.all(arr);
This part just pushes functions into the arr variable, it doesn't run them, thus not creating Promises and never filling the items array.
On the other hand, the other examples run the function someFn:
const arr = [...Array(10).keys()].map(someFn)
This fills the arr array with 10 executions of someFn (map executes them with the current value (0-9), the index (also 0-9) and the array itself).
for (let i = 0; i < 10; i++) {
await someFn();
}
And this obviously runs someFn in a loop.
To make the first example work, push the result of the function into the array:
(async () => {
const items = [];
const someFn = async () => {
const v = await Promise.resolve(10);
items.push(Math.random());
return Promise.resolve(v * 10);
}
const arr = [];
for (let i = 0; i < 10; i++) {
arr.push(someFn()); // <-- () added
}
await Promise.all(arr);
console.log("item 1", items);
})();
you’re pushing someFn but you want someFn(). note we’re calling the function.

Categories

Resources