Javascript not parsing array passed over through function - javascript

I have an array which is passed through a function using javascript, I can't see anything wrong with the code but it doesn't pass over the first array correctly so it can be parsed.
The idea is the first array is 56 items, it then calls the parseData function which is supposed to split this array into chunks of 7.
Here is the two functions:
static async validateRowValues() {
let data = [];
await cy.get('tr > td > div.dlCell')
.each(function (row) {
let d = row.get(0).innerText;
data.push(d);
});
console.log(data);
let response = await this.parseData(data);
console.log({response});
}
static async parseData(tData) {
console.log(tData);
let array = [];
let coll_array = [];
debugger;
await tData.forEach(async (v, index) => {
await array.push(v);
if (index % 6 === 0 && index !== 0) {
await coll_array.push(array);
array = [];
}
});
return coll_array;
}
The first console.log within parseData does return the 56 items, however by the time it reaches tData.forEach it has completely lost its data, and the parsing returns an empty array when it returns coll_array.
If anyone has any ideas?

As of now I will take it you getting your data fine in array.
ex. arr = [1,2,3,.....58]
Use below code to split in chunks of 7
arr = arr.reduce((acc,data,index)=>{
if(index==0 || index%7==0) acc.push([])
acc[acc.length-1].push(data)
return acc
},[])
The above code will return
arr = [ [1,..,7], [8,...14], ....]

We have resolved this.
It turns out everything in Cypress is a promise so the first function needed to have a .then
static async validateRowValues() {
let data = [];
await cy.get('tr > td > div.dlCell')
.each(function (row) {
let d = row.get(0).innerText;
data.push(d);
}).then(() => {
this.parseData(data);
});
}

Related

Order Pokémons from 1 to 20 not a random order

When I run this code it give me a random order of Pokémons. I don't know where is the problem.
Thank you so much.
for (var i = 1; i <= 20; i++) {
apiPokemon("https://pokeapi.co/api/v2/pokemon/"+i);
async function apiPokemon(urlPokemon) {
const response = await fetch(urlPokemon);
const dataPokemon = await response.json();
var id = dataPokemon.id;
var name = dataPokemon.name;
console.log(id, name);
}
}
First thing's first: "Why are they coming back in random order?" - Because you are not awaiting each response. Instead you are firing off all 20 async calls which can come back in any order, so that's why they are logging in a random order.
In order to fix this, there are a few changes I'd recommend:
Extract your apiPokemon function out of your loop so it doesn't get recreated for each loop iteration
Return the entire data object from your apiPokemon function
Add all of the apiPokemon requests to an array and await them with Promise.all()
Log the output of the Promise.all() and you'll see that they will now always be in correct order
async function apiPokemon(urlPokemon) {
const response = await fetch(urlPokemon);
const dataPokemon = await response.json();
return dataPokemon;
}
async function getPokemon(startIndex, stopIndex) {
let requests = [];
for (let i = startIndex; i <= stopIndex; i++) {
requests.push(apiPokemon("https://pokeapi.co/api/v2/pokemon/"+i));
}
let pokemonList = await Promise.all(requests);
for (let pokemon of pokemonList) {
console.log(pokemon.id, pokemon.name);
}
}
getPokemon(1, 20)

Get array from function

I have tried to get the array I can log inside the function to be used outside. I’m using sapper. Have been stuck with this for multiple hours now, so it was time to reach out to the community!
let dvd = [];
let artistName = [];
let searchString = [];
onMount(async () => {
const res = await fetch(`artists/all`);
const data = await res.json();
const newdata = data.map(x => {
if (x.titles.length > 0) {
dvd.push(x.titles[0].graphics.dvd)
artistName.push(x.titles[0])
}
})
})
let searchTerm = '';
async function getData() {
searchString.push(artistName.filter(d => d.artistName === searchTerm));
console.log(searchString)
}
If I understand the question correctly, your issue is that the updated value of searchString is not being applied to the DOM. This is due to how Svelte's reactivity works. From the tutorial:
Because Svelte's reactivity is triggered by assignments, using array methods like push and splice won't automatically cause updates.
You should update getData to assign to searchString instead of calling push.
async function getData() {
searchString = [...searchString, artistName.filter(d => d.artistName === searchTerm)];
console.log(searchString)
}

How to wait for all the code in a promise to finish before resolving it? (but a little more complex)

Sorry for the very confusing question, I have this code that gets information from a website without any node modules or libraries. It is a list of users separated into different pages use ?page= at the end of the URL. I have managed to iterate through the pages and split up the raw HTML just right. However, my promise resolves before all the data is collected. How can I wait for everything to finish before I resolve the promise? I have tried countless solutions, but none seem to work. Please don't ask to use a node package, as my goal is to not use one :) A friend helped with the regex and splitting it up. Here is the code I am using:
function getData() {
return new Promise((resolve, reject) => {
let final = [] //the array of users returned in the end
const https = require("https"), url = "https://buildtheearth.net/buildteams/121/members";
https.get(url + "?page=1", request => { //initial request, gets the number of user pages.
let rawList = '';
request.setEncoding("utf8"),
request.on("data", data => {rawList += data}),
request.on("end", () => {
if(request = (request = (request = rawList.substring(rawList.indexOf('<div class="pagination">'))).substring(0, request.indexOf("</div>"))).match(/<a(.+)>(.+)<\/a>/g)) {
for(let t = parseInt(request[request.length - 1].match(/(\d+)(?!.*\d)/g)), a = 1; a < t + 1; a++) { //iterates through member pages
https.get(url + "?page=" + a, request2 => { //https request for each page of members
let rawList2 = '';
request2.setEncoding('utf8'),
request2.on("data", data => {rawList2 += data}),
request2.on("end", () => {
let i = rawList2.match(/<td>(.+)<\/td>/g); //finds table in HTML
if (i)
for (var t = 1; t < i.length; t += 3) //iterates through rows in table
console.log(i[t].replace(/<td>/g, "").replace(/<\/td>/g, "")), /* logs element to the console (for testing) */
final.push(i[t].replace(/<td>/g, "").replace(/<\/td>/g, "")); //pushes element to the array that is resolved in the end
})
})
}
}
resolve(final) //resolves promise returning final array, but resolves before elements are added with code above
})
})
})
}
If this helps, here is the website I am trying to get info from.
I am still a little new to JS so if you could help, I would really appreciate it :)
I ended up turning each action into an async function with a try and catch block and then chained the functions together with .then() For the base (getting data from a website) I took inspiration from an article on Medium. Here is the site I am pulling data from, and here is the function to get data from a website:
const getData = async (url) => {
const lib = url.startsWith('https://') ? https : http;
return new Promise((resolve, reject) => {
const req = lib.get(url, res => {
if (res.statusCode < 200 || res.statusCode >= 300) {
return reject(new Error(`Status Code: ${res.statusCode}`));
}
const data = [];
res.on('data', chunk => data.push(chunk));
res.on('end', () => resolve(Buffer.concat(data).toString()));
});
req.on('error', reject);
req.end();
});
};
and then I got the number of pages (which can be accessed by appending ?page=<page number> to the end of the url) with this this function:
const pages = async () => {
try {
let html = await getData('https://buildtheearth.net/buildteams/121/members',);
let pages = await (html = (html = html.substring(html.indexOf('<div class="pagination">'))).substring(0, html.indexOf("</div>"))).match(/<a(.+)>(.+)<\/a>/g)
let pageCount = await parseInt(pages[pages.length - 1].match(/(\d+)(?!.*\d)/g))
return pageCount
} catch (error) {
console.error(error);
}
}
and then I used the page count to iterate through the pages and add the HTML of each to an array with this function:
const getPages = async pageCount => {
let returns = []
try {
for (page = 1; page <= pageCount; page++) {
try {
let pageData = await getData('https://buildtheearth.net/buildteams/121/members?page=' + page)
returns.push(pageData)
} catch (error) {
return error
}
}
} catch (error) {
return error
} finally {return returns}
}
and then I iterated through the array of strings of HTML of each page, and extracted the data I needed out of each with this function which would return the list of members I need:
const iteratePages = async pages => {
if (!Array.isArray(pages)) return
try {
let returns = []
await pages.forEach(page => {
let list = page.match(/<td>(.+)<\/td>/g);
if (list)
for (var element = 1; element < list.length; element += 3)
returns.push(list[element].replace(/<td>/g, "").replace(/<\/td>/g, ""));
})
return returns
} catch (error) {
return error
}
}
And then it was a matter of chaining each together to get the array I needed:
pages().then(pageCount => getPages(pageCount)).then(pages => iteratePages(pages)).then(finalList => {console.log(finalList); console.log(finalList.length)})

Filter and map Promise with async

I got promise object, that returned by sequelize.findAndCountAll(), and I wanna filter the object before mapping them.
Here's my code:
async function getCountTask(id){
return await Tasks.count({
where: {
studentID: id,
status: 'Done',
grade: ['A', 'B']
}
});
}
let totalStudent = [];
await Promise.all(
listStudent.filter(async (f) => {
const count = await getCountTask(f.id);
if(count <= 3){
return false;
}
return true;
}).map(async (e) => {
let obj = {};
obj.id = e.id;
obj.age = e.age;
obj.status = 'Great';
totalStudent.push(obj);
})
)
My expectation listStudent contain 5 data, but after filter, it will only contain 3 data because 2 another didn't pass the condition. So for ther final result the totalStudent containing 3 data.
But what I got from the code above is, the totalStudent have data exactly like listStudent.
It's because it process the map first and then the filter, so the map process data that didn't filter yet.
How do I can make it become filter first and then map the data.
Thanks in advance.
Filter / map etc, don't support async in the way your using them.
eg. filter expects either a true or false, your returning promise async functions is always a promise.
await Promise.all(
listStudent.filter(async (f) => { <<<<---- This makes filter always return true
const count = await getCountTask(f.id);
Looking at your code, a simple solution is just remove the filter, and just use map..
eg..
await Promise.all(
listStudent.map(async (f) => {
const count = await getCountTask(f.id);
if (count <= 3) return; //don't want this one.
let obj = {};
obj.id = f.id;
obj.age = f.age;
obj.status = 'Great';
totalStudent.push(obj);
})
)
Seen as Promise.all returns an array, you can also avoid using push on totalStudent,.
eg.
totalStudent = await Promise.all(
listStudent.map(async (f) => {
const count = await getCountTask(f.id);
if (count <= 3) return; //don't want this one.
let obj = {};
obj.id = f.id;
obj.age = f.age;
obj.status = 'Great';
return obj;
})
)
The advantage of the above is that the return order is also maintained.

different representation of arrays in chrome console

Here is my code
let loadInitialImages = ($) => {
let html = "";
let images = new Array();
const APIURL = "https://api.shutterstock.com/v2/images/licenses";
const request = async() => {
const response = await fetch(APIURL, { headers: auth_header() } );
const json = await response.json();
json.data.map((v) => images.push(v.image.id)); //this is where the problem is
}
request();
// I can see the contents of the array when I log it.
console.log(images);
// But I can't see any elements when logging this way:
images.map((id) => console.log(id));
}
Everything is working fine here but the problem is when I'm pushing the elements into the array is goes out of the array braces [] below is the screenshot of my array:
I'm not able to loop through the array here.
This is how a usual Array looks like in Console
See Array braces here. Elements appear to be inside [1, 2, 3]
Since your request function is async you need to treat its result as a Promise.
This is also the reason why you see it represented differently in the chrome console. An empty array gets printed, but the references in the console are updated dynamically, so you can still expand it and see the contents.
If you want to log the contents of the array statically, you could use something like JSON.stringify to print it. This will print a string representation of the exact state of the array at the time of logging.
// You will need to check the output in the browser console.
// Your code could be reduced to this:
const a = [];
setTimeout(() => a.push(1, 2), 100);
console.log('a:', a);
// A filled array logs differently:
const b = [1, 2];
console.log('b:', b);
// Stringify gives you a fixed state:
const c = [];
setTimeout(() => c.push(1, 2), 100);
console.log('c:', JSON.stringify(c));
Regarding your code, on top of waiting for request(), if you are using map you should take advantage of how it works. You can use it to generate your entire array without using push for example. If you still want to use your array and push() to it, you should use json.data.forEach instead of json.data.map since it doesn't duplicate the array.
// Making your function `async` so you can `await` for the `request()`
let loadInitialImages = async ($) => {
let html = "";
const APIURL = "https://api.shutterstock.com/v2/images/licenses";
const request = async () => {
const response = await fetch(APIURL, { headers: auth_header() } );
const json = await response.json();
// Array.map will return a new array with the results of applying
// the given function to the original array, you can use that as
// an easy way to return your desired array.
return json.data.map((v) => v.image.id);
}
// Since request() is async, you need to wait for it to complete.
const images = await request();
// Array.forEach lets you iterate over an array without generating a
// copy. If you use map here, you would be making an unneeded copy
// of your images array.
images.forEach(i => console.log(i));
}
Snippet below demonstrates your issue (your case is arr1, you want arr2).
In case loadInitialImages can't be async use arr3 scenario.
async function main(){
let arr1 = [], arr2 = [], arr3 = [];
const getArray = ()=> (new Promise(resolve=>setTimeout(()=>{resolve([1,2,3])},1000)))
async function request(arr, number){
var result = await getArray();
result.forEach((el)=>(arr.push(el)))
console.log(`inner${number}`, arr)
return result;
}
request(arr1, 1);
console.log("outer1", arr1)
await request(arr2, 2);
console.log("outer2", arr2)
request(arr3, 3).then(()=>{
console.log("then3",arr3)
})
console.log("outer3", arr3)
}
main();
I think the probleme is that the console.log() is fired before the array is populated, and becose the console.log work with reference it print both state of array (when it's empty, and after populating it with .map)
you can test this code ?
the console is directly after the loop
let loadInitialImages = ($) => {
let html = "";
let images = new Array();
const APIURL = "https://api.shutterstock.com/v2/images/licenses";
const request = async() => {
const response = await fetch(APIURL, { headers: auth_header() } );
const json = await response.json();
json.data.map((v) => images.push(v.image.id)); //this is where the problem is
console.log(images);
}
request();
}
let loadInitialImages = ($) => {
let html = "";
let images = new Array();
const APIURL = "https://api.shutterstock.com/v2/images/licenses";
const request = async() => {
const response = await fetch(APIURL, { headers: auth_header() } );
const json = await response.json();
json.data.map((v) => images.push(v.image.id)); //this is where the problem is
console.log(images);
}
request();
}
loadInitialImages();

Categories

Resources