I have an api call that produces the follow results in the console (after pairing it down using map() ).
{…}
CHANGE24HOUR: "$ 11.16"
CHANGEDAY: "$ 3.31"
CHANGEPCT24HOUR: "6.73"
CHANGEPCTDAY: "1.90"
FROMSYMBOL: "Ξ"
HIGH24HOUR: "$ 183.38"
HIGHDAY: "$ 183.38"
However, no matter what I try I can't get at it's properties. The object is called 'coinStats'.
I have tried the following:
coinStats.HIGHDAY = undefined
coinStats['HIGHDAY'] = undefined
coinStats["HIGHDAY"] = undefined
Tried to convert it to an array to see if that would help using
Object.values(coinStats) // Would not work
I am sure the answer is so simplistic. But I am just not sure what it is?
The original raw api results are in the following shape:
(1) […]
0: Object { CoinInfo: {…}, RAW: {…}, DISPLAY: {…} }
length: 1
<prototype>: [
The info I am looking for is in DISPLAY -> USD. I used a map() function to return that sub-object.
The code I am using to fetch the data is essentially the following:
const API = 'https://min-api.cryptocompare.com/data/top/mktcapfull?tsym=USD&page=1';
fetch(API)
.then(results => results.json())
.then(coinData => {
const view = coinData.Data.filter(obj => { return obj.CoinInfo.Name === TRX});
})
const coinFeedAbridged = view.map(item => item.DISPLAY.USD);
const coinStats = coinFeedAbridged[0];
console.dir(coinStats);
I can't access coinStats.HIGHDAY for example... I get 'undefined'.
The fetch call is async, so you can't access the view variable until the call is finished. The code calls the async function and runs the next line, so view variable will be undefined. You have to handle the data inside the fetch callback function, where the data is known.
const API = 'https://min-api.cryptocompare.com/data/top/mktcapfull?tsym=USD&page=1';
fetch(API)
.then(results => results.json())
.then(coinData => {
const view = coinData.Data.filter(obj => {
return obj.CoinInfo.Name === 'TRX'
});
const coinFeedAbridged = view.map(item => item.DISPLAY.USD);
const coinStats = coinFeedAbridged[0];
console.log(coinStats);
})
}
You can test it out in this fiddle. https://jsfiddle.net/gran7ptk/1/
Change line - Add inverted commas to Text "TRX".
const view = coinData.Data.filter(obj => { return obj.CoinInfo.Name
=== TRX});
to
const view = coinData.Data.filter(obj => { return obj.CoinInfo.Name === "TRX"});
Related
I am trying to map multiple Database entries to an Object, so I can filter it.
async autocomplete(interaction) {
const focusedValue = interaction.options.getFocused();
let choices = await items.findAll({
attributes: ['itemID', 'desc_de'] });
const jsondata = JSON.stringify(choices);
const json = JSON.parse(jsondata);
const itemObj = json.map(function(item) {
return {
itemID: item.itemID,
item_desc: item.desc_de,
};
});
const filtered = itemObj.filter(choice => choice.item_desc.toLowerCase().includes(focusedValue.toLowerCase()));
await interaction.respond(
filtered.map(choice => ({name: choice.item_desc, value: choice.itemID})),
);
The issue I am having is, apparently choice.item_desc is NULL somehow, I am unsure, what I am doing wrong here.
The error is
TypeError: Cannot read properties of null (reading 'toLowerCase')
Before I had an object, which was only holding the itemID and with that it was working fine.
focusedValue may be null to which you cannot apply .toLowerCase(). Try setting a default value to that given that function exists outside the scope of the code provided
I'm working with React/Redux and want to use the same Promise result to set two different states. Here's my code:
useEffect(() => {
fetchData().then((resp) => {
Object.freeze(resp);
const var1 = resp[0];
const var2 = parseData(resp[0]);
props.setAction1(var1);
props.setAction2(var2);
});
}, []);
The content of resp is an array of dicts:
Array [ (95) […] ]
0: Array(95) [ {…}, {…}, {…}, … ]
0: Object { date: "6/1/11", open: 21.45673929 }
1: Object { date: "6/2/11", open: 21.02743338 }
2: Object { date: "6/3/11", open: 20.64964196 }
etc...
I know that in Javascript object are mutable, so I attempted to make resp immutable with Object.freeze(resp). var1 should be the result with string dates and var2 should be the result with date converted to Date objects with the parseData function, whose content is:
function parseData(data) {
data.map((x) => (x.date = new Date(x.date)));
return data;
}
My problem is that, when I run this function, var1 === var2. The parseData function affected not only var2 but also var1. I don't understand how this happens, especially as I froze the resp object. This is likely an issue with immutability, but I'm unsure where else to go.
Your parseData function currently returns the same array object, in which the objects were mutated. If you want your objects to be immutable, you should not have code like x.date =. Instead do this:
function parseData(data) {
return data.map((x) => ({...x, {date: new Date(x.date)} }) );
}
I don't think Object.freeze is what you're looking for. In the end, the objects still reference the same location in memory, even if they are frozen. You should make a copy of the data to separate the two so you can alter either of them without affecting the other one.
fetchData().then((resp) => {
const ref1 = resp;
/* Alternatively you can also use `Object.assign` */
const ref2 = [ ...resp ].map(obj => ({ ...obj }));
}
Like #JohnD mentioned, you can also do that in your parseData function which might be a little tidier. ;)
I don't believe Object.freeze(resp); will freeze nested objects. You could try the following, but I believe it is confusing to read.
useEffect(() => {
fetchData().then((resp) => {
Object.freeze(resp);
Object.freeze(resp[0]);
const var1 = resp[0];
const var2 = parseData(resp[0]);
props.setAction1(var1);
props.setAction2(var2);
});
}, []);
Alternatively, you could change your parseData function to return a copy using the spread operator:
function parseData(data) {
[...data].map((x) => (x.date = new Date(x.date)));
return data;
}
Object.freeze will freeze the direct properties of the object you pass. It will not do this recursively, so giving it an array or nested object will not do what you expect.
The parseData function affected not only var2 but also var1.
No. Here, var1 === var2 because your parseData code does not modify the object at all, but only a single property of it. It's the same object instance all-along. When you set x.date = new Date(x.date), the x itself does not change.
Not sure if that's going to be necessary, but you could freeze each individual object in resp, using map().
const freezeAll = items => items.map(item => Object.freeze(item));
const parseData = items => items.forEach(item => item.date = new Date(item.date));
// fetch data, process, and cache it
const data = fetchData().then(parseData).then(freezeAll);
// use data
useEffect(() => {
data.then(items => {
props.setAction1(items[0]);
props.setAction2(items[0]);
});
}, []);
I'm trying to test my Promise with chai. My promise returns an array of objects like this:
[
{id: 1, Location: 'some where'},
{id: 2, Location: 'over the rainbow'},
]
I keep getting this error but my test still passes:
UnhandledPromiseRejectionWarning: AssertionError: expected [ Array(7) ] to have deep property 'Location'
My code:
describe('Function myPromise', () => {
it(`should returns object with property Location`, () => {
expect(myPromise(myParams)).to.eventually.have.deep.property('Location');
});
});
I've also tried:
to.eventually.have.deep.property.include('Location');
to.eventually.have.property('Location');
to.eventually.be.an('array').and.have.property('Location');
return expect(myPromise(myParams)).to.eventually.have('Location');
have.deep.property is applicable for checking object but seems like you checked the array.
Let me illustrate with a simple example
const fixtures = [
{id: 1, Location: 'some where'},
{id: 2, Location: 'over the rainbow'},
];
// I tested the first element
expect(fixtures[0]).to.have.deep.property('Location'); // passed
If you want to check every single element, you probably need a loop.
Updates
To use loop to check every element, the first thing to do is to grab the fixtures from the promise function. In this way, I'm using async/await but you can use promise too.
describe("Function myPromise", () => {
it(`should returns object with property Location`, async () => {
const fixtures = await myPromise(myParams); // get the fixtures first
// looping and check each element
fixtures.forEach(fixture =>
expect(fixture).to.have.deep.property("Location"); // no need to use `eventually.have.deep`
);
});
});
Hope it helps
I've figured out how to avoid that error. It's simpler and more straightforward than I thought:
describe('Function myPromise', () => {
it(`should returns object with property Location`, () => {
myPromise(myParams)
.then((results) => {
expect(results).to.be.an('array');
results.forEach((result) => {
expect(result).to.have.property('Location');
});
});
});
});
You can try using Joi (or chai-joi in your case) to make schema validation very easy. I would definitely try this if you're going to be having many tests with object schema validation.
Well a promise is handled asynchronously. I believe you are missing the .then() method in which will return the promise(rejected or fulfilled).
describe('Function myPromise', () => {
it(`should returns object with property Location`, () => {
expect(myPromise(myParams).then(data => data).to.eventually.have.deep.property('Location');
});
});
I have the following code and what I am trying to do is to fetch some data from a database (which seems to be successful, I tried to log it), then arrange that data and send an array as a response. My class:
export class Controller {
currentQuestions: IQuestionArranged[] = []; // This should be my array that I am trying to send as response
constructor(){ }
public async startNewGame(req: express.Request, res: express.Response): Promise<void> {
let factory: RepositoryFactory = new RepositoryFactory();
let repository: RepositoryInterface = factory.createRepository();
let questionsFetched: IQuestionFetched[] = await repository.fetchQuestions(); // array of questions as they were fetched
// let currentQuestions: IQuestionArranged[] = []; // <<<<<
// forEach through fetched questions to arrange them
questionsFetched.forEach(currentQuestion => {
// check if the current question already exists in the array
if (this.currentQuestions.filter(e => e.id == currentQuestion.questionId).length != 0) {
// if yes -> find the question and add the new answer with its id
this.currentQuestions
.filter(e => e.id == currentQuestion.questionId)
.map(e => e.answers.push({
id: currentQuestion.answerId,
answer: currentQuestion.answer
}));
} else {
// if no -> add the new question with the new answer
this.currentQuestions.push({
id: currentQuestion.questionId,
question: currentQuestion.question,
answers: [{
id: currentQuestion.answerId,
answer: currentQuestion.answer
}]
});
}
});
res.send(this.currentQuestions);
}
}
But I got this error that says "UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'currentQuestions' of undefined" (full error description here). It seems to be working just fine if I use a local variable (the one marked with 5 left arrows as a comment) instead of the class property, which makes me think I am not using it properly but I do not know where my mistake is...
Can you try the arrow function with a function expression? E.g =>
public startNewGame = async (req: express.Request, res: express.Response) => {
// Your code goes here.
}
I found a cleaner solution. What I had to do was to bind the "this" valiable like so:
constructor() {
this.startNewGame = this.startNewGame.bind(this);
}
Input: an array of username strings
Needed output: an array of Javascript Objects that correspond to each username in the input. The properties for these JS objects is to be built from two API calls for each username (I am using $.getJSON calls. Suggestions welcome).
I have an array of usernames for the Twitch API:
let users = ["OgamingSC2", "storbeck", "comster404"] // actual list is longer
I want to use the Array.prototype.map() higher order function to create an array of objects like
let userObjects = [ {...}, {...}, {...}, ... ]
where each object looks like:
{
username: 'storbeck' // <-- Added by me
stream: null, // <-- Added by first API call
_links: { // <-- Added by first API call
self:'https://api.twitch.tv/kraken/streams/storbeck',
channel:'https://api.twitch.tv/kraken/channels/storbeck'
}
logo: 'http:// ... png' // <-- Added by second API call
}
These are the two API call functions that return the $.getJSON Promises:
let getStreamInfo = (username) => {
return $.getJSON('https://api.twitch.tv/kraken/streams/'+username+'?callback=?')
.then((x) => x) // should I include this then?
}
let getUserInfo = (twitchObject) => {
return $.getJSON('https://api.twitch.tv/kraken/users/'+ twitchObject.user )
}
What I have so far in my code, which isn't resulting in the intended objects is:
let userObjects = users.map((user)=>{
return getStreamInfo(user)
.done((data) => {
let result = {
username: user,
data: data
}
console.log(JSON.stringify(result)) // prints out the intended object so far
return result
})
})
Now when I print out the contents of userObjects, I get:
"{}"
"{}"
"{}"
// ... and so on
Going further, I'd like to chain userObjects and add more to each JS object from whatever I get in the getUserInfo function.
I'd like to go into how this can be done with functional Javascript, but this isn't necessary.
You are on the right way, you need only small edit on your functions.
let getStreamInfo = (username) => {
return $.getJSON('https://api.twitch.tv/kraken/streams/'+username+'?callback=?');
}
let getUserInfo = (user) => {
return $.getJSON('https://api.twitch.tv/kraken/users/'+ user);
}
let userObjects = [];
The core function instead needs Promise synchronization:
users.map((user)=>{
Promise.all(getStreamInfo(user), getUserInfo(user)).then((data)=>{
let obj = {
username: user,
stream: data[0].stream,
_links: data[0]._links,
logo: data[1].logo
}
userObjects.push(obj);
});
});