Issue with dictionary in React - javascript

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.

Related

Merging 2 arrays, with specified items

I stuck with joining 2 arrays.
I fetch data from 2 API, in response I got 2 different arrays, what I want to achive is one array with joined and selected arguments.
const skills = ref([]);
const entries = axios.get(`https://cdn.contentful.com/spaces/${space_id}/environments/${environment_id}/entries?access_token=${access_token}`);
const assets = axios.get(`https://cdn.contentful.com/spaces/${space_id}/environments/${environment_id}/assets?access_token=${access_token}`);
axios
.all([entries, assets])
.then(
axios.spread((...responses) => {
const responseEntries = responses[0].data.items.map(
(item) => item.fields
);
const responseAssets = responses[1].data.items.map(
(item) => "https:" + item.fields.file.url
);
const checkEntries = Array.isArray(responseEntries);
const checkAssets = Array.isArray(responseAssets);
console.log(checkEntries);
console.log(checkAssets);
console.log(responseEntries);
console.log(responseAssets);
for (let i = 0; i < responseEntries.length; i++) {
skills[i].value = [
responseAssets[i],
responseEntries[i].title,
responseEntries[i].list,
responseEntries[i].description,
];
}
})
)
.catch((error) => console.log(error));
I'm getting error:
TypeError: Cannot set properties of undefined (setting 'value')
Here is what I got in console, and the how arrays looks
Thank you! Your answer help me to rethink that problem and finally I solved it in different way I added a value to first array.
for (let i = 0; i < responseEntries.length; i++) {
responseEntries[i].url = responseAssets[i];
}
skills.value = responseEntries;
Your problem is certainly inside the for loop, where you try:
skills[i].value = [...]
at that point, skills[i] doesn't exist so you have to initialize it before into an object, so:
for (let i = 0; i < responseEntries.length; i++) {
skills.value[i] = {}
skills.value[i] = [
responseAssets[i],
responseEntries[i].title,
responseEntries[i].list,
responseEntries[i].description,
];
}
I still don't understand the first line, though, const skills = ref([]); where, imho, should just be const skills = []

Don't make functions within a loop no-loop-func -React JS

I am trying to find index of array using lodash locationbar. but my react console showing some warnings. can be resolve?
let wishListData = wishList.result;
let j = 0; const jMax = wishListData.length;
for (; j < jMax; j++) {
var index = _.findIndex(products.result, function (product) {
return product.id === wishListData[j]['pid']
});
if (index !== -1) {
products.result[index]['isWishList'] = true;
}
}
Iterate over wishList.result with forEach instead of a for loop, and you'll avoid the warning:
wishListData.forEach(({ pid }) => {
const index = _.findIndex(products.result, ({ id }) => id === pid);
if (index !== -1) {
products.result[index].isWishList = true;
}
});
Note that this is a linter warning, not a Javascript error. Your code works, the linter just considers it to be confusing - better to use array methods instead of loops when possible.
Also, feel free to remove the _ library and just use built-in Javascript methods instead, if you want:
wishListData.forEach(({ pid }) => {
const product = products.result.find(({ id }) => id === pid);
if (product) {
product.isWishList = true;
}
});
Or, for an O(N) solution instead of an O(N^2) solution, figure out all pids first, then iterate over the products:
const pids = new Set(wishListData.map(({ pid }) => pid));
products.result.forEach((product) => {
if (pids.has(product.id)) {
product.isWishList = true;
}
});
You can try something like this as well:
Instead of mutating product in products.result[index]['isWishList'] = true;, you should create new object to minimize side-effect.
Also, instead of looping on wishListData, you can create a list of PIDs and just check index. If this list is created outside, you can create list of PIDs outside as well. That will reduce processing it every time
const wishListPID = wishList.result.map((x) => x.pid);
const result = products.result.map((product) => {
const isWishList = wishListPID.indexOf(product.id) !== -1;
return { ...product, isWishList }
});

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 });
}

Randomize async array

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

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