Randomize async array - javascript

i have a bug somewhere in my code and iam not able to find it.
At first, this is my method:
async getRandomMeals() {
let that = this;
let meals = [];
let recipeList = await this.appData.getRecipeList(); // {title: "someTitle", ...}, {title: "meal",...} from local stroage
let excludedIngredients = await this.appData.getItem("excludedIngredientsArray") || []; // get from local storage
let mealsProfile = await this.appData.getItem("mealsProfile"); // get from local storage
let filterRecipeList = recipeList;
if (excludedIngredients.length > 0) {
filterRecipeList = this.filterRecipeListByExcludes(recipeList, excludedIngredients)
}
for (let i = 0; i <= 6; i++) {
meals.push(mealsProfile);
}
meals.forEach((day) => {
day.forEach((daytime) => {
daytime.meals = filterRecipeList.filter(x => {
return x.daytime.includes(daytime.slug.toLowerCase())
&& x.difficultyNumber <= daytime.difficulty
}).sort(function () { // <-- this dont work
return 0.5 - Math.random()
});
});
});
console.log(meals);
return meals;
}
My problem:
Everything works fine so far, but the meals should be randomized and for some reasons the randomization dont work.
The problem isnt, that i cant randomize the array. The randomize works great itself, but not on this "filterRecipeList" array.
I would be really happy if someone got a hint or something why the randomize stuff dont work.
Edit:
Also this dont work:
filterRecipeList = filterRecipeList.sort(function () {
return 0.5 - Math.random()
});
Edit 2:
I now got a dirty solution, which looks like this:
await this.appData.setItem("tmpMealPlan", mealPlan); // save to local storage
let finalMealPlan = await this.appData.getItem("tmpMealPlan"); // get again from local storage
finalMealPlan.forEach((day) => {
day.forEach((daytime) => {
daytime.meals = daytime.meals.sort(function () {
return 0.5 - Math.random()
}).slice(0,4);
});
});
return finalMealPlan;
For some reason this works fine, but it looks aweful :D

Related

Issue with dictionary in React

Relatively new to React/Javascript in general so any help would be appreciated.
I currently have an application which is fetching data for multiple items from an API. buys is a list of dictionaries(called buy here) with fields asset, units and price (among other things).
buys.map(async buy => {
var data = await queryCoinGeckoAPI(buy);
var market_price = data.market_data.current_price.aud;
var price_change = data.market_data.price_change_24h_in_currency.aud;
var price_change_percentage = data.market_data.price_change_percentage_24h_in_currency.aud;
var profit = buy.units === 0 || buy.price === 0 ? 0 : market_price * buy.units - buy.price;
newDictionary[buy.asset] = {
asset: buy.asset,
market_price: market_price,
price_change: price_change,
price_change_percentage: price_change_percentage,
profit: profit
};
});
That's all fine and when I come to log newDictionary:
Hooray it works!
However, the problem comes when I'm not trying to access these values in the dictionary. If I try calling newDictionary['bitcoin'] or Object.keys(newDictionary) or even
for(let key in newDictionary) {
console.log(key);
console.log(newDictionary[key]);
}
for example I get no output.
Undefined returned
Not particularly sure why and couldn't find an answer on this online...
I chose a dictionary because I would like to be able to update my current state (I hope this is how you use the spread operator):
setBuys(
buys.map(b => {
{...b, ...newDictionary[b.asset]};
})
);
Full function in case you need it :
useEffect(() => {
const refreshData = async() => {
var d = await Promise.all(
buys.map(async buy => {
var data = await queryCoinGeckoAPI(buy);
var market_price = data.market_data.current_price.aud;
var price_change = data.market_data.price_change_24h_in_currency.aud;
var price_change_percentage = data.market_data.price_change_percentage_24h_in_currency.aud;
var profit = buy.units === 0 || buy.price === 0 ? 0 : market_price * buy.units - buy.price;
return {
asset: buy.asset,
market_price: market_price,
price_change: price_change,
price_change_percentage: price_change_percentage,
profit: profit
};
})
)
var newDictionary = {};
for (let i = 0; i < d.length; i++) {
newDictionary[d[i].asset] = d[i];
}
console.log(newDictionary);
setBuys(
buys.map(
b => {
{...b, ...newDictionary[b.asset]}
}
)
)
// toast.info('Market updated', {});
}
const interval = setInterval(() => {
refreshData();
}, 60000);
return () => clearInterval(interval);
})
Thanks!
The problem with your final setBuys call is that you're simply not returning anything from the mapper function since it's using {} braces.
You'll want
setBuys(
buys.map(
b => (
{...b, ...newDictionary[b.asset]}
)
)
)
instead (b => (, not b => {).
(I'm not even sure if dictionaries are a thing in React).
You are probably thinking of objects, perhaps that can help you with googling anything related in the future.
I think the spread operator is the culprit here, try replacing it with this (forgive the formatting):
.maps( b => {{b: newArray[b.asset]}}
Try adding to some log statements to see the acual contents of newArray, maybe it is actualy an object instead of an array.

How to make react stop duplicating elements on click

The problem is that every time I click on an element with a state things appear twice. For example if i click on a button and the result of clicking would be to output something in the console, it would output 2 times. However in this case, whenever I click a function is executed twice.
The code:
const getfiles = async () => {
let a = await documentSpecifics;
for(let i = 0; i < a.length; i++) {
var wrt = document.querySelectorAll("#writeto");
var fd = document.querySelector('.filtered-docs');
var newResultEl = document.createElement('div');
var writeToEl = document.createElement('p');
newResultEl.classList.add("result");
writeToEl.id = "writeto";
newResultEl.appendChild(writeToEl);
fd.appendChild(newResultEl);
listOfNodes.push(writeToEl);
listOfContainers.push(newResultEl);
wrt[i].textContent = a[i].data.documentName;
}
}
The code here is supposed to create a new div element with a paragraph tag and getting data from firebase firestore, will write to the p tag the data. Now if there are for example 9 documents in firestore and i click a button then 9 more divs will be replicated. Now in total there are 18 divs and only 9 containing actual data while the rest are just blank. It continues to create 9 more divs every click.
I'm also aware of React.Strictmode doing this for some debugging but I made sure to take it out and still got the same results.
Firebase code:
//put data in firebase
createFileToDb = () => {
var docName = document.getElementById("title-custom").value; //get values
var specifiedWidth = document.getElementById("doc-width").value;
var specifiedHeight = document.getElementById("doc-height").value;
var colorType = document.getElementById("select-color").value;
parseInt(specifiedWidth); //transform strings to integers
parseInt(specifiedHeight);
firebase.firestore().collection("documents")
.doc(firebase.auth().currentUser.uid)
.collection("userDocs")
.add({
documentName: docName,
width: Number(specifiedWidth), //firebase-firestore method for converting the type of value in the firestore databse
height: Number(specifiedHeight),
docColorType: colorType,
creation: firebase.firestore.FieldValue.serverTimestamp() // it is possible that this is necessary in order to use "orderBy" when getting data
}).then(() => {
console.log("file in database");
}).catch(() => {
console.log("failed");
})
}
//get data
GetData = () => {
return firebase.firestore()
.collection("documents")
.doc(firebase.auth().currentUser.uid)
.collection("userDocs")
.orderBy("creation", "asc")
.get()
.then((doc) => {
let custom = doc.docs.map((document) => {
var data = document.data();
var id = document.id;
return { id, data }
})
return custom;
}).catch((err) => {console.error(err)});
}
waitForData = async () => {
let result = await this.GetData();
return result;
}
//in render
let documentSpecifics = this.waitForData().then((response) => response)
.then((u) => {
if(u.length > 0) {
for(let i = 0; i < u.length; i++) {
try {
//
} catch(error) {
console.log(error);
}
}
}
return u;
});
Edit: firebase auth is functioning fine so i dont think it has anything to do with the problem
Edit: This is all in a class component
Edit: Clicking a button calls the function createFileToDb
I think that i found the answer to my problem.
Basically, since this is a class component I took things out of the render and put some console.log statements to see what was happening. what i noticed is that it logs twice in render but not outside of it. So i took the functions out.
Here is the code that seems to fix my issue:
contain = () => {
const documentSpecifics = this.waitForData().then((response) => {
var wrt = document.getElementsByClassName('writeto');
for(let i = 0; i < response.length; i++) {
this.setNewFile();
wrt[i].textContent = response[i].data.documentName;
}
return response;
})
this.setState({
docs: documentSpecifics,
docDisplayType: !this.state.docDisplayType
})
}
As for creating elements i put them in a function so i coud reuse it:
setNewFile = () => {
const wrt = document.querySelector(".writeto");
const fd = document.querySelector("#filtered-docs");
var newResultEl = document.createElement('div');
newResultEl.classList.add("result");
var wrtEl = document.createElement('p');
wrtEl.classList.add("writeto");
fd.appendChild(newResultEl);
newResultEl.appendChild(wrtEl);
}
The firebase and firestore code remains the same.
the functions are called through elements in the return using onClick.

How do I console.log json information?

quite new to working with both api's and javascript. I'm trying to console.log out the amount of times a friend of mine has died in rust, but all items are named "name:" An img of what I mean:
I'm using axios to call the api, here's the code:
var statsApi = 'http://api.steampowered.com/ISteamUserStats/GetUserStatsForGame/v0002/?';
var statsAppId = 'appid='+252490+'&'
var statsSteamApiKey = 'key='+process.env.STEAM_API+'&';
var statsUserArg = 'steamid='+userID;
var steamStatsApi = statsApi + statsAppId + statsSteamApiKey + statsUserArg;
axios
.get(steamStatsApi)
.then((res) =>{
message.channel.send("AAAA");
// console.log(res['data']['playerstats']['stats']['name' == 'deaths']);
for(re in res['data']['playerstats']['stats']) {
if(re['name'] === 'deaths') {
console.log('test');
console.log(re['value']);
}
else {
console.log('Fel');
}
}
})
.catch((err) => {
console.error('Error:', err);
})
Again if anyone knows how to get the amount of 'deaths' I'd be super grateful.
Turns out it was quite easy, if anyone has this problem just use the following code which I am about to provide:
var index = -1;
for(var i = 0; i < Object.keys(res['data']['playerstats']['stats']).length; i++) {
if(res['data']['playerstats']['stats'][i]['name'] === 'deaths') {
index = i;
var userDeaths = res['data']['playerstats']['stats'][index]['value'];
break;
}
}
What this does is that it loops trough every object named "name:" until it finds 'deaths' in one of them. I change i to that and now i'm able to print out the value which i'm looking for.
Please check this code:
let res = [{ name: "deaths", value: 2694}, {name:"bullet_fired", value: 343328}];
let i = 0;
for(i=0;i<res.length;i++){
if(res[i].name =="deaths"){
console.log(res[i].value);
}
}
Here res variable will contain your json values.

Async object does not get returned from getInitialProps despite success elsewhere

I'm just starting to figure out React by putting together a bit of code from different parts, and from an online course.
I'm using React, Next and Axios to get an API from a cryptocurrency server.
The main issue I'm facing is:
I am able to console.log(coinObjects) under getInitialProps, and it displays the object correctly
Despite this, coinObjects does not get rendered in {this.props.coinObjects}
As a possible clue, linksArr does get rendered in {this.props.linksArr}
The code I have is as follows:
class MainIndex extends Component {
static async getInitialProps(props) {
// setup - empty array and list of coins
const coinList = ["NEO", "ETH", "BTC"];
const numCoins = coinList.length;
const coinObjects = [];
const linksArr = [];
const isServer = typeof window === "undefined";
// API GET
const baseUrl = "https://min-api.cryptocompare.com/data/histohour?";
for (let coinName of coinList) {
linksArr.push(
baseUrl.concat("fsym=", coinName, "&tsym=", "USD", "&limit=", "3")
);
}
const getObj = async linksArr => {
try {
let res = await axios.all(linksArr.map(l => axios.get(l)));
for (let i = 0; i < linksArr.length; i++) {
coinObjects[coinList[i]] = res[i].data.Data;
}
} catch (err) {
console.error(err);
}
};
await getObj(linksArr);
console.log(coinObjects);
// Return updated arrays
if (isServer) {
return { coinObjects, numCoins, linksArr };
} else {
return {};
}
}
render() {
return (
<Layout>
<h2>
CoinObject has {this.props.coinObjects.length} coins
// Returns 0
<br />
LinksArr has {this.props.linksArr.length} links
// Returns 3
</h2>
</Layout>
);
}
}
Could anyone please help me? I've exhausted all the Google searches, Stackoverflow posts and coding friends that I can find (just 1). I can't figure out what's wrong, and I hope that this isn't a silly question because I've been tweaking and changing things extensively, but have yet to figure out what's wrong.
Here the coinObject is set to an array:
const coinObjects = [];
But later is treated as an Object:
coinObjects[coinList[i]] = res[i].data.Data;
That means that you would want to add to the array like this:
for (let i = 0; i < linksArr.length; i++) {
let data = res[i].data.Data;
let name = coinList[i];
coinObjects.push({ name: name, data: data });
}

Limit number of records in firebase

Every minute I have a script that push a new record in my firebase database.
What i want is delete the last records when length of the list reach a fixed value.
I have been through the doc and other post and the thing I have found so far is something like that :
// Max number of lines of the chat history.
const MAX_ARDUINO = 10;
exports.arduinoResponseLength = functions.database.ref('/arduinoResponse/{res}').onWrite(event => {
const parentRef = event.data.ref.parent;
return parentRef.once('value').then(snapshot => {
if (snapshot.numChildren() >= MAX_ARDUINO) {
let childCount = 0;
let updates = {};
snapshot.forEach(function(child) {
if (++childCount <= snapshot.numChildren() - MAX_ARDUINO) {
updates[child.key] = null;
}
});
// Update the parent. This effectively removes the extra children.
return parentRef.update(updates);
}
});
});
The problem is : onWrite seems to download all the related data every time it is triggered.
This is a pretty good process when the list is not so long. But I have like 4000 records, and every month it seems that I screw up my firebase download quota with that.
Does anyone would know how to handle this kind of situation ?
Ok so at the end I came with 3 functions. One update the number of arduino records, one totally recount it if the counter is missing. The last one use the counter to make a query using the limitToFirst filter so it retrieve only the relevant data to remove.
It is actually a combination of those two example provided by Firebase :
https://github.com/firebase/functions-samples/tree/master/limit-children
https://github.com/firebase/functions-samples/tree/master/child-count
Here is my final result
const MAX_ARDUINO = 1500;
exports.deleteOldArduino = functions.database.ref('/arduinoResponse/{resId}/timestamp').onWrite(event => {
const collectionRef = event.data.ref.parent.parent;
const countRef = collectionRef.parent.child('arduinoResCount');
return countRef.once('value').then(snapCount => {
return collectionRef.limitToFirst(snapCount.val() - MAX_ARDUINO).transaction(snapshot => {
snapshot = null;
return snapshot;
})
});
});
exports.trackArduinoLength = functions.database.ref('/arduinoResponse/{resId}/timestamp').onWrite(event => {
const collectionRef = event.data.ref.parent.parent;
const countRef = collectionRef.parent.child('arduinoResCount');
// Return the promise from countRef.transaction() so our function
// waits for this async event to complete before it exits.
return countRef.transaction(current => {
if (event.data.exists() && !event.data.previous.exists()) {
return (current || 0) + 1;
} else if (!event.data.exists() && event.data.previous.exists()) {
return (current || 0) - 1;
}
}).then(() => {
console.log('Counter updated.');
});
});
exports.recountArduino = functions.database.ref('/arduinoResCount').onWrite(event => {
if (!event.data.exists()) {
const counterRef = event.data.ref;
const collectionRef = counterRef.parent.child('arduinoResponse');
// Return the promise from counterRef.set() so our function
// waits for this async event to complete before it exits.
return collectionRef.once('value')
.then(arduinoRes => counterRef.set(arduinoRes.numChildren()));
}
});
I have not tested it yet but soon I will post my result !
I also heard that one day Firebase will add a "size" query, that is definitely missing in my opinion.

Categories

Resources