unable to shuffle array correctly in react - javascript

I am trying to shuffle an array in react . I fetch the data from the api and then I want to mount the data pictures as a shuffled array in my screen and not in the order I fetched them .
This is my code :
useFetch.js
import {React , useState , useEffect} from 'react';
export default function useFetch() {
const [ pokemon,setPokemon] = useState([]);
const [shuffled,setShuffled]= useState([]);
useEffect(()=>{
const fetchPokemon = async () =>{ //here I fetch my pokemon
const promises = [];
for (let i=1;i<=10;i++){
let url = `https://pokeapi.co/api/v2/pokemon/${i}`;
let response = await fetch(url);
let result = await response.json();
promises.push(result);
}
const data = await Promise.all(promises);
setPokemon(...pokemon,data); //successfully sets the pokemon data
}
const shufflePokemon = ()=>{ //here I try to shuffle the pokemon array and return a random on mount
fetchPokemon();
let randomArray= pokemon.map((poke,index)=>{ //this is what I am trying to do to shuffle the array but it is not correct
let j = Math.floor(Math.random() * (index + 1));
let temp = poke[index];
poke[index] = poke[j];
poke[j] = temp;
})
setShuffled(...shuffled,randomArray);
}
shufflePokemon(); //call shuffle on mount
},[])
return {shuffled} //returns shuffled array of objects
}
In my code above in the shufflePokemon function I try to give an idea of what needs to be done but the code is obviously not correct . I would appreciate your help

You can shuffle the array as soon as you get responses from api.
useEffect(() => {
const shuffle = (array) => {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
};
const fetchPokemon = async () => {
//here I fetch my pokemon
const promises = [];
for (let i = 1; i <= 10; i++) {
let url = `https://pokeapi.co/api/v2/pokemon/${i}`;
let response = await fetch(url);
let result = await response.json();
promises.push(result);
}
const data = await Promise.all(promises);
shuffle(data);
setPokemon(data);
};
fetchPokemon();
}, []);

Fischer-Yates shuffle is usually what's used.
It looks like you're close, but the algorithm pulls a random item from the end of the array, not the beginning as you're doing.
const randomArray = Array.from(fetchPokemon()).forEach((v,i,a) => {
const r = a.length - 1 - Math.trunc(Math.random() * i);
[ a[i], a[r] ] = [ a[r], a[i] ];
});

Related

FetchAPI parallel API calls, making URL array with for loop doesn't work, manual array works. Can't understand why

I'm trying to form an url array to then perform parallel fetches usign .map and Promise.all
The code in itself works if I try it with a manually made array but not if I try to form an array (for pagination) with a for loop.
I get the maxnumber of pages from the headers of the first fetch and with that number I use a for loop to add an increasing numbered page until the final amount of pages.
I'm realizing trying to make a minimally reproducible example here that this isn't 'working' in a sense, when I do this in my server with this sample code, it works and
console.log(urls);
actually shows the looped urls and it does seem to be in array format, since I used push seems impossible not to be? But then again, when performing the urls.map it doesn't work at all and trying to do something else, like .slice to urls it doesn't work either, so I'm sensing I'm not forming the array I think I am?
I can't figure it out.
async function smt() {
var url = 'https://jsonplaceholder.typicode.com/todos/';
var urls = [];
var firstFetch = fetch(url)
.then(function(e) {
var maxpages = 5;
//var maxpages = e.get.headers('TotalPages'); < I would get the maxpages here
for (let i = 1; i < maxpages; i++) {
urls.push(url + i)
}
});
console.log(urls);
var data = await Promise.all(urls.map(async url => {
const res = await fetch(url);
return res.json();
}))
console.log(data);
var other = [
'https://jsonplaceholder.typicode.com/todos/1',
'https://jsonplaceholder.typicode.com/todos/2',
'https://jsonplaceholder.typicode.com/todos/3',
'https://jsonplaceholder.typicode.com/todos/4',
]
var data2 = await Promise.all(other.map(async url => {
const res = await fetch(url);
return res.json();
}))
console.log(data2);
}
smt();
The problem was with the .then() function I had no clue but I tested everypart until it worked.
async function smt() {
var url = 'https://jsonplaceholder.typicode.com/todos/';
var urls = [];
var firstFetch = await fetch(url)
var maxpages = 5;
//var maxpages = firstFetch.get.headers('TotalPages'); < I would get the maxpages here
for (let i = 1; i < maxpages; i++) {
urls.push(url + i)
}
console.log(urls);
var data = await Promise.all(urls.map(async url => {
const res = await fetch(url);
return res.json();
}))
console.log(data);
var other = [
'https://jsonplaceholder.typicode.com/todos/1',
'https://jsonplaceholder.typicode.com/todos/2',
'https://jsonplaceholder.typicode.com/todos/3',
'https://jsonplaceholder.typicode.com/todos/4',
]
var data2 = await Promise.all(other.map(async url => {
const res = await fetch(url);
return res.json();
}))
console.log(data2);
}
smt();
/**** Updated Answer ****/
async function smt() {
var url = 'https://jsonplaceholder.typicode.com/todos/';
var urls = [];
//var firstFetch = await fetch(url) < would get headers
var maxpages = 5;
//var maxpages = firstFetch.get.headers('TotalPages'); < I would get the maxpages here
for (let i = 1; i < maxpages; i++) {
urls.push(url + i)
}
console.log(urls);
let promises = urls.map(async url => {
let a = await fetch(url);
return a.json()
})
var data = await Promise.all(promises)
console.log(data);
}
smt();
I think this is a better approach since you can limit concurrency when you do the urls.map outside the Promise.all.
I have done it with p-limit in node it works great.
const limit = pLimit(8);
let promises = urls.map(url => {
return limit(async() => {
let a = await fetch(url, optionsGet)
return a.json()
})
});

JS recursive mergeSort with objects

Hihi,
My question is simple. I'm trying to implement mergesort but having a limitation with the structure of my project
I have a file called sort.js
const selSort = require('./selectionSort');
const bubSort = require('./bubbleSort');
const inSort = require('./insertionSort');
const merSort = require('./mergeSort');
const sort = (arr) => {
const array = arr;
return {
...selSort(array),
...bubSort(array),
...inSort(array),
...merSort(array),
};
};
As you can see I'm trying to implement multiple sort algorithm and make an object
well, now while I started doing the MergeAlgorithm (a recursive sort algorithm) I faced with the problem of the objects in JavaScript
const merge = (left, right) => {
const result = [];
while (left.length && right.legnth) {
result.push(left[0] < right[0] ? left.shifth() : right.shifth());
}
return [...result, ...left, ...right];
};
const merSort = (array) => ({
mergeSort: () => {
if (array.length === 1) {
return array;
}
const middle = Math.floor(array.length);
const leftArr = array.slice(0, middle);
const rightArr = array.slice(middle);
return merge(this(leftArr).mergeSort(), this(rightArr).mergeSort());
},
});
module.exports = merSort;
I cant call mergeSort() and pass to it the left and right array without calling itself again.
Is there anyway to solve this?
I tried using this but it didn't worked
I think there is no way to achive what I want
Why are you using the spread operator ( ... ) all the time ? Also why are you reassigning the arr argument to a constant?
This should work just fine
const selSort = require('./selectionSort');
const bubSort = require('./bubbleSort');
const inSort = require('./insertionSort');
const merSort = require('./mergeSort');
const sort = (arr) => {
return {
selSort(arr),
bubSort(arr),
inSort(arr),
merSort(arr),
};
};
Also for the sorting code this should work
function mergeSort(unsortedArray) {
if (unsortedArray.length <= 1) {
return unsortedArray;
}
const middle = Math.floor(unsortedArray.length / 2);
const left = unsortedArray.slice(0, middle);
const right = unsortedArray.slice(middle);
return merge(mergeSort(left,), mergeSort(right));
}
function merge(left, right) {
let resultArray = [],
leftIndex = 0,
rightIndex = 0;
while (leftIndex < left.length && rightIndex < right.length) {
if (left[leftIndex] < right[rightIndex]) {
resultArray.push(left[leftIndex]);
leftIndex++;
} else {
resultArray.push(right[rightIndex]);
rightIndex++;
}
}
return resultArray
.concat(left.slice(leftIndex))
.concat(right.slice(rightIndex));
}
module.exports = mergeSort;

How to call async Function until condition is true using javaScript?

I have such a problem, I am using API call which returns Fake store data, Everything works fine I am pushing received data into arr array, my aim is to call repeatCall() function until arr.length will be arr.length === 100, Also the problem is, sometimes I want to filter received data based on condition and then push it into arr array, What would be the optimal solution in this case?
let arr = [];
async function repeatCall() {
const req = await fetch(`https://fakestoreapi.com/products`);
const resp = await req.json();
arr.push(...resp)
console.log(arr.length)
}
repeatCall()
Use an if condition on arr.length it will fetch api till the length is equal to 100.
let arr = [];
async function repeatCall() {
const req = await fetch(`https://fakestoreapi.com/products`);
const resp = await req.json();
arr.push(...resp)
console.log(arr.length)
if(arr.length < 100){
repeatCall();
}
}
As for filtering the data before pushing it to array.
let arr = [];
async function repeatCall() {
const req = await fetch(`https://fakestoreapi.com/products`);
const resp = await req.json();
resp.foreach(item => {
if(condition){
arr.push(item);
}
else{
console.log("don't push")
}
})
if(arr.length < 100){
repeatCall();
}
console.log(arr.length)
}

Function moving on before AXIOS promise response is returned

I am having a problem with the responses of this function. The function is supposed to return a concatenated list of all ITEMID for a game. This list can change depending on what is being sold at a time. The first axios call is used to grab the first 1000 item numbers and the number of pages. I would then have to make axios calls for the amount of pages to retrieve all of the item numbers. Seems a bit tedious but I don't think there is a way around this.
The problem is the function isn't running the other pages and only returns the first 1000 items before the other responses come back. Is there a better way to implement this function?
export const getAllItemID = (region) => {
let finalList = [];
let pages = 0;
let currentPage = 1;
let region_buy_id = `URL For Call`;
axios
.get(region_buy_id)
.then(json => {
pages = json.headers['x-pages'];
for(let k in Object.values(json.data)){
finalList.push(json.data[k]);
}
currentPage++;
})
.then(()=>{
while(currentPage <= pages) {
let region_buy_id = `URL For Call`;
axios
.get(region_buy_id)
.then((json) => {
for(let k in Object.keys(json.data)){
finalList.push(json.data[k]);
}
});
currentPage++;
}
})
.then(()=>{
return finalList;
});
}
Updated Function
export async function getAllItemID(region) {
let finalList = [];
let pages = 0;
let currentPage = 1;
let region_buy_id = `URL`;
await axios
.get(region_buy_id)
.then(json => {
pages = json.headers['x-pages'];
for(let k in Object.values(json.data)){
finalList.push(json.data[k]);
}
currentPage++;
});
while(currentPage <= pages) {
let region_buy_id = `URL`;
await axios
.get(region_buy_id)
.then((json) => {
for(let k in Object.keys(json.data)){
finalList.push(json.data[k]);
}
});
currentPage++;
}
}

JavaScript Asynchronous in Redux..?

Action in fuction (action for data imported from firstore)
Here you get index data.
export async function getUserIndex (data){
let db = loadFB().firestore();
console.log(data)
let date = moment(data).utc().format()
let query = db.collection('users').where("create_date", ">", date)
console.log(query)
return await query.get().then(docs=>{
let result = docs.size
console.log("result!!", result)
return result
})
}
components in function
async getuserIndex_component(date){
let number =""
number = await userAction.getUserIndex(date)
console.log("number",number)
return await number
}
const {user_list} = this.props; //user_list = [{id:aa, pw:bb},{id:cc, pw:dd}...} data
let data = user_list
let groups = {}
let number =0
for (let index_data of data) {
let date = moment(index_data.create_date).format("YYYY-MM-DD").slice(0, 10)
let index = this.getuserIndex_component(date) //<==here log Promise{<pendding>}
console.log(index)
if (groups[date]) {
let group = groups[date];
group.push({ index: index-number, ...index_data });
} else {
number =0;
groups[date] = [{ index: index-number, ...index_data }]
}
number++;
}
const dates = Object.keys(groups)
const user_list_result = []
for (let date of dates) {
user_list_result.push(...(groups[date]))
}
return(...)
I am using redux. in action
Made a "getUserIndex()."
The component will then access the "getUserIndex" and retrieve data. But there is only pending in the console.log, and the data I want does not appear.
In this situation, I would like to know how to deal with async in for of.
I want to know what part I'm making a mistake.
In summary, functions in components are executed later than the render. So the index in the render does not contain the data. What is the solution to this?
As getuserIndex_component is asynchronous, it will return a promise. You will need to wait for that promise to resolve before the index result is available.
Firstly, you should be able to simplify getuserIndex_component to
async getuserIndex_component(date) {
let number = await userAction.getUserIndex(date)
console.log("number", number)
return number
}
And when you remove the console.log it could simply be
async getuserIndex_component(date) {
return userAction.getUserIndex(date)
}
As I mentioned in the comments. You can send off all of the async calls, then just wait for all of the promises to resolve via Promise.all.
Something like this. (I couldn't test any of this, but it should demonstrate the general idea.)
const { user_list } = this.props; //user_list = [{id:aa, pw:bb},{id:cc, pw:dd}...} data
let data = user_list
let groups = {}
let number = 0
const dates = []
const promises = data.map((index_data) => {
const date = moment(index_data.create_date).format("YYYY-MM-DD").slice(0, 10)
dates.push(date) // To use after all promises resolve.
return this.getuserIndex_component(date) // The index promise.
})
const indexes = await Promise.all(promises)
for (let i = 0; i < dates.length; i++) {
const index = indexes[i]
const date = dates[i]
console.log(index)
if (groups[date]) {
let group = groups[date];
group.push({
index: index - number,
...index_data
});
} else {
number = 0;
groups[date] = [{
index: index - number,
...index_data
}]
}
number++;
}
const dates = Object.keys(groups)
const user_list_result = []
for (let date of dates) {
user_list_result.push(...(groups[date]))
}
return (...)

Categories