How can I access fetched data with multiple layers? - javascript

I need help trying to access the data the is fetched from an API? The only thing I can do is game.results which I show lower. Everything else I try just outputs "undefined". I'm not sure what else to try that is why I am asking because I have runout of ways to fix this.
function showGames()
{
const options = {
method: 'GET',
headers: {
'X-RapidAPI-Host': 'sportspage-feeds.p.rapidapi.com',
'X-RapidAPI-Key': '<KEY>'
}
};
let url = 'https://sportspage-feeds.p.rapidapi.com/games?league=NBA&date=2022-04-28';
fetch(url, options)
.then(res => {
return res.json();
})
.then((data) => {
const games = data;
let game = games;
let result = `<h2> NBA GAMES</h2>`;
console.log(game.results);
for(let i = 0; i < game.length; i++)
{
thisGame = game[i];
result += `
<div>
<h1>${game.results.summary}</h1>
</div>
`;
}
document.getElementById("games").innerHTML = result;
});
}
window.onload=showGames();
This is what one of the outputs of data.results looks like. I'm not sure how I can access the data in here like summary. I've tried using game.results.summary but it just outputs undefined.
[ { schedule: { date: '2022-04-28T23:00:00.000Z', tbaTime: false },
summary: 'Philadelphia 76ers # Toronto Raptors',
details:
{ league: 'NBA',
seasonType: 'postseason',
season: 2021,
conferenceGame: true,
divisionGame: true },
status: 'final',
teams: { away: [Object], home: [Object] },
lastUpdated: '2022-04-29T01:38:41.138Z',
gameId: 284279,
venue:
{ name: 'Scotiabank Arena',
city: 'Toronto',
state: 'ON',
neutralSite: false },
odds: [ [Object] ],
scoreboard:
{ score: [Object],
currentPeriod: 4,
periodTimeRemaining: '0:00' } },

This statement in your loop:
game = game[i];
overwrites the array game and breaks the whole thing. You need a variable to use in the loop that is separate from the game array:
let thisGame = game[i];

Related

JavaScript, fetch, API

i want to do the following: get a random name with fetch from this website https://swapi.dev/api/people/, which i did and i can see it in my html page then i want also to get a random planet, here i need to access the homeworld key, and to return the link, before returning the link i formatted to get a random url and from this one i also have to show the name of the planet on my page. The first fetch works fine, at least i think but the 3rd .then() is not working or at least i don't know how to access the information from the homeworld url. This is my first time trying fetch() and it will be nice if you guys can help me telling where i did wrong in code and maybe different solutions but not so complicated :D tnks
let randomNumber = Math.floor(Math.random()*9)
const fetchPromise = fetch("https://swapi.dev/api/people/");
let test
let test2
let planets = document.querySelector('#age')
fetchPromise
.then((response) => {
if (!response.ok) {
throw new Error(`Http error: ${response.status}`);
}
return response.json();
})
.then((json) => {
console.log(json.results[randomNumber].name)
showRandomUserData(json)
test = json.results[0].homeworld
test = test.slice(0, -2)
// console.log(test + randomNumber + "/");
// console.log(test + "/" + randomNumber + "/");
test = test + randomNumber + "/";
return fetch(test)
// return fetch("https://swapi.dev/api/planets/2/");
})
.then(response => response.json()).then(json =>
{ test2=json.name
console.log(test2);
planets.innerHTML = test2
})
showRandomUserData = (randomUser) => {
document.querySelector("#name").innerHTML =
randomUser.results[randomNumber].name;
}
Solved
Here's a simple solution that uses fetch() to grab data from both those URLs and then insert all the people and the one planet that is returned into your web page:
function myFetch(...args) {
return fetch(...args).then(response => {
if (!response.ok) {
throw new Error(`fetch failed with status ${response.status}`);
}
return response.json();
});
}
Promise.all([
myFetch("https://swapi.dev/api/people/"),
myFetch("https://swapi.dev/api/planets/2/")
]).then(([people, planet]) => {
const peopleDiv = document.getElementById("people");
let peopleHTML = "";
for (let p of people.results) {
peopleHTML += `<div>${p.name}</div>`;
}
peopleDiv.innerHTML = peopleHTML;
const planetDiv = document.getElementById("planets");
let planetHTML = `<div>${planet.name}</div>`;
planetDiv.innerHTML = planetHTML;
}).catch(err => {
console.log(err);
});
<div id="people"></div>
<hr>
<div id="planets"></div>
As for using the results, the people URL returns a structure that looks like this:
{
count: 82,
next: 'https://swapi.dev/api/people/?page=2',
previous: null,
results: [
{
name: 'Luke Skywalker',
height: '172',
mass: '77',
hair_color: 'blond',
skin_color: 'fair',
eye_color: 'blue',
birth_year: '19BBY',
gender: 'male',
homeworld: 'https://swapi.dev/api/planets/1/',
films: [Array],
species: [],
vehicles: [Array],
starships: [Array],
created: '2014-12-09T13:50:51.644000Z',
edited: '2014-12-20T21:17:56.891000Z',
url: 'https://swapi.dev/api/people/1/'
},
{
name: 'C-3PO',
height: '167',
mass: '75',
hair_color: 'n/a',
skin_color: 'gold',
eye_color: 'yellow',
birth_year: '112BBY',
gender: 'n/a',
homeworld: 'https://swapi.dev/api/planets/1/',
films: [Array],
species: [Array],
vehicles: [],
starships: [],
created: '2014-12-10T15:10:51.357000Z',
edited: '2014-12-20T21:17:50.309000Z',
url: 'https://swapi.dev/api/people/2/'
}
}
So, you have people.results which is an array and you can access people.results[n] to get an item from that array. That item will be an object which has properties like .name, .height, etc...
The specific planet URL you show returns a single planet object like this:
{
name: 'Alderaan',
rotation_period: '24',
orbital_period: '364',
diameter: '12500',
climate: 'temperate',
gravity: '1 standard',
terrain: 'grasslands, mountains',
surface_water: '40',
population: '2000000000',
residents: [
'https://swapi.dev/api/people/5/',
'https://swapi.dev/api/people/68/',
'https://swapi.dev/api/people/81/'
],
films: [
'https://swapi.dev/api/films/1/',
'https://swapi.dev/api/films/6/'
],
created: '2014-12-10T11:35:48.479000Z',
edited: '2014-12-20T20:58:18.420000Z',
url: 'https://swapi.dev/api/planets/2/'
}
So, you access properties on that object as in planet.name.
Notice that the people results are paged. There are 82 total results, but only 10 come in this first result. The rest come with results for other pages such as https://swapi.dev/api/people/?page=2.
Similar to this answer but using async/await to avoid callback hell. If you can, try using this approach. Why?
Excellent recommendation in that answer by jfriend00 to use Promise.all instead of separate fetch calls, as that enables fetching to happen in parallel. To know more.
sandbox to test and try
const fetchData = async (...args) => {
try {
const response = await fetch(...args);
return response.json();
} catch (err) {
throw new Error(`fetch failed with status ${err?.message}`);
}
};
const updateDOM = (people, planet) => {
document.getElementById("people").innerHTML =
people.results.reduce((s, p) => s + `<div>${p.name}</div>`, "");
document.getElementById("planets").innerHTML = `<div>${planet.name}</div>`;
};
const populateData = async () => {
try {
const [people, planet] = await Promise.all([
fetchData("https://swapi.dev/api/people/"),
fetchData("https://swapi.dev/api/planets/2/"),
]);
// do stuff with 'people' or 'planet'
// example, get
// const firstPersonsHomeworld = people.results[0].homeworld;
// console.log(firstPersonsHomeworld);
// or
// const planetName = planet.name;
// console.log(planetName);
updateDOM(people, planet);
} catch (err) {
// errorHandler(err);
console.error(err);
}
};
// start app
populateData();

How to create array and pass them in Node.js

I'd like to pass fetched data like following array.
{ category: ['Animals','Vehicles']
difficulty: ['easy','medium']
}
I tried something like following, getCategory.push(quiz.getCategory(2)),
But I encountered following errors.
Unexpected token .
I confused how to pass array like data.
If someone has opinon,please let me know.
Thanks
const API_KEY="https://opentdb.com/api.php?amount=10&type=multiple";
const fetch = require('node-fetch');
const Quiz=require("../public/javascripts/quiz");
module.exports={
getQuiz:function(resp){
fetch(API_KEY)
.then(response => response.json())
.then(json => { const quiz = new Quiz(json);
resp.send({
getCategory:quiz.getCategory(1),
getCategory.push(quiz.getCategory(2)),
getDifficulty:quiz.getDifficulty(1),
getDifficulty.push(quiz.getDifficulty(2))
});
});
}
};
My class is following.
class Quiz {
constructor(quizData){
this._quizzes = quizData.results;
this._correctAnswersNum = 0;
}
getNumOfQuiz(){
return this._quizzes.length;
}
getCategory(index){
return this._quizzes[index-1].category;
}
getDifficulty(index){
return this._quizzes[index-1].difficulty;
}
}
module.exports = Quiz;
fetch(API_KEY) returned something like following.
results:
[ { category: 'Animals',
type: 'multiple',
difficulty: 'easy',
question: 'What do you call a baby bat?',
correct_answer: 'Pup',
incorrect_answers: [Array] },
{ category: 'Vehicles',
type: 'multiple',
difficulty: 'medium',
question: 'Which supercar company is from Sweden?',
correct_answer: 'Koenigsegg',
incorrect_answers: [Array] },
{ category: 'Entertainment: Board Games',
type: 'multiple',
difficulty: 'hard',
question: 'Which board game was first released on February 6th, 1935?',
correct_answer: 'Monopoly',
incorrect_answers: [Array] }]
I didn't see how exactly we need to use the Class methods in this implementation..
But we can go as follows.
This is the data we are receiving
const data = [ { category: 'Animals',
type: 'multiple',
difficulty: 'easy',
question: 'What do you call a baby bat?',
correct_answer: 'Pup',
incorrect_answers: [Array] },
{ category: 'Vehicles',
type: 'multiple',
difficulty: 'medium',
question: 'Which supercar company is from Sweden?',
correct_answer: 'Koenigsegg',
incorrect_answers: [Array] },
{ category: 'Entertainment: Board Games',
type: 'multiple',
difficulty: 'hard',
question: 'Which board game was first released on February 6th, 1935?',
correct_answer: 'Monopoly',
incorrect_answers: [Array] }];
The data structure we want to send as in response.
const finalResponse = {
category : [],
difficulty : []
}
Looping over actual data, and collect the data in finalResponse. May be if you already know the index of data you can make the use if Class as mentioned in question. For now we have used data array prototype method forEach to loop over each datum as shown below.
data.forEach(function({category, difficulty}){
finalResponse.category.push(category);
finalResponse.category.push(difficulty);
})
finally send the collected data as response
res.send(finalResponse);
Answer 1 (quick fix):
const API_KEY="https://opentdb.com/api.php?amount=10&type=multiple";
const fetch = require('node-fetch');
const Quiz=require("../public/javascripts/quiz");
module.exports={
getQuiz:function(resp){
fetch(API_KEY)
.then(response => response.json())
.then(json => { const quiz = new Quiz(json);
resp.send({
getCategory:[quiz.getCategory(1), quiz.getCategory(2)],
getDifficulty:[quiz.getDifficulty(1),quiz.getDifficulty(2)]
});
});
}
};
Answer 2 (correct way):
class Quiz : introduce new method to get array | getPropArray() // I'm bad at naming
class Quiz {
constructor(quizData){
this._quizzes = quizData.results;
this._correctAnswersNum = 0;
}
getNumOfQuiz(){
return this._quizzes.length;
}
getCategory(index){
return this._quizzes[index-1].category;
}
getDifficulty(index){
return this._quizzes[index-1].difficulty;
}
/**
* get array of prop (property)
* #param {string} prop
* #param {number} start
* #param {number} end
*/
getPropArray(prop, start=1, end=this._quizzes.length){
let res = [];
for (let i=start-1; i<=end-1; i++){
res.push(this._quizzes[i][prop]);
}
return res;
}
}
module.exports = Quiz;
now you can add new property to your response if you want, idk what logic you wanna use to choose result[index] but here^ you can apply a range.
const API_KEY="https://opentdb.com/api.php?amount=10&type=multiple";
const fetch = require('node-fetch');
const Quiz=require("../public/javascripts/quiz");
module.exports={
getQuiz:function(resp){
fetch(API_KEY)
.then(response => response.json())
.then(json => { const quiz = new Quiz(json);
resp.send({
getCategory: quiz.getPropArray('category' ),
getDifficulty: quiz.getPropArray('difficulty')
});
});
}
};

Proxy in mobx -state-tree

I'm trying to build a simple budgeting app.
Whenever I insert this model into my app. I get a proxy for the expenses. Where is the flaw in my thinking?
I have an action on the Budget.js
when I print it in the useEffect this is what console.log outputs for the expenses a proxy.
I'm expecting it to print the actual data from the initial state.
React.useEffect(() => {
budget.addDummyData()
console.log(budget.expenses)
}, [])
[[Handler]]: Object
[[Target]]: Array(0)
[[IsRevoked]]: false
//////////////////////////////////////////////////////////////////////
//SubCategory
const SubCategory = types
.model('SubCategory', {
id: types.maybeNull(types.string, ''),
name: types.maybeNull(types.string, ''),
amount: types.maybeNull(types.number, 0)
})
const SubCategoryStore = types.model({ subCategory: types.optional(SubCategory, {}) })
export default SubCategoryStore
/////////////////////////////////////////////////////////////////////////
//Category.js
const Category = types
.model('Category', {
id: types.maybeNull(types.string, ''),
name: types.maybeNull(types.string, ''),
subCategories: types.array(SubCategory)
})
const CategoryStore = types.model({ category: types.optional(Category, {}) })
export default CategoryStore
///////////////////////////////////////////////////////////////
// Budget
const Budget = types
.model('Budget', {
totalIncome: 200,
expenses: types.array(Category)
// incomes: types.optional(types.array(Category), [])
}).actions({
addDummyData() {
self.expenses.push(initialStateExpenses)
}
})
const BudgetStore = types.model({ budget: types.optional(Budget, {}) })
export default BudgetStore
const initialStateExpenses = {
id: '123',
name: 'Food',
subCategories: [
{
id: '1314',
name: 'Grocery',
amount: 250
},
{
id: '1442',
name: 'Restaurants',
amount: 50
}
]
}
expenses is of type Category[], you are passing an object. I assume you want to set the expenses from subCategories. If so you can try this
addDummyData() {
initialStateExpenses.subCategories.forEach(ex => self.expenses.push(ex))
}
or
addDummyData() {
self.expenses = initialStateExpenses.subCategories
}
A better approach would be to pass the initialStateExpenses via args to the addDummyData function so your model doesn't depend on external variables
addDummyData(initialStateExpenses) {
initialStateExpenses.subCategories.forEach(ex => self.expenses.push(ex))
}
or
addDummyData(initialStateExpenses) {
self.expenses = initialStateExpenses.subCategories
}
then use it like
budget.addDummyData(initialStateExpenses)

Inserting new element into empty JSON object

I'm trying to insert data into an empty JSON array, but am having trouble. I'm defining the array in the constructor, then making a couple get requests to the back-end when the page loads, and after getting the response I want to add the new array element to the existing. This is the code I am using:
constructor() {
super();
this.state = {
sds: []
}
}
componentDidMount() {
axios.get('/userData', {
params: {
user: this.props.auth.user.name
}
}).then(res => {
for(var i=0; i<res.data[0].chemID.split(',').length; i++){
if(res.data[0].chemID.split(',')[i] != 0){
axios.get('/chemData', {
params: {
id: res.data[0].chemID.split(',')[i]
}
//This is where I want to insert the data
}).then(res => this.sds += ({
id: i,
title: res.data[0].chemName,
selected: false,
key: 'sds'
}))
}
}
})
}
+= doesn't work like that. Use a spread operator to copy the previous contents of the array, then add the new object in manually -
}).then((res) => {
const newThing = {
id: i,
title: res.data[0].chemName,
selected: false,
key: 'sds'
};
this.setState(prevState => ({
sds: [...prevState.sds, newThing]
}))
}
You should never try to mutate the state yourself, always use setState. In this case, you can pass a function as the first parameter, which provides the previous state. That way, you can ensure whatever was in this.state.sds is preserved, and your new object is added to that array.
You can use array.push().
this.state.sds.push(obj);
If you are not using react setState method than, you need to refer any state variable using this.state.variableName.
You need to add to your array by using the push() method like this:
constructor() {
super();
this.state = {
sds: []
}
}
componentDidMount() {
axios.get('/userData', {
params: {
user: this.props.auth.user.name
}
}).then(res => {
for(var i=0; i<res.data[0].chemID.split(',').length; i++){
if(res.data[0].chemID.split(',')[i] != 0){
axios.get('/chemData', {
params: {
id: res.data[0].chemID.split(',')[i]
}
//This is where I want to insert the data
}).then(res => {
this.state.sds.push({
id: i,
title: res.data[0].chemName,
selected: false,
key: 'sds'
})
})
}
}
})
}
You can try using the next example:
this.state.sds[this.state.sds.length] = {
id: i,
title: res.data[0].chemName,
selected: false,
key: 'sds'
}
[Edited]
Like #larz said, you must use the setState method to avoid unexpected result of your code.
var newSds = this.stage.sds;
newSds[newSds.length] = {
id: i,
title: res.data[0].chemName,
selected: false,
key: 'sds'
};
this.setState({ sds: newSds});
You can get more information about the lifecycle in react here and the "state updates are merged" here

Add snapshot.val & snapshot.key to an array while I´m subscribed

I´m gonna break my head with a stone ^^"
I have this code:
this.af.database.list('/Documentos', { preserveSnapshot: true })
.subscribe(snapshots => {
snapshots.forEach(snapshot => {
console.log(snapshot.key, snapshot.val());
});
})
With that I extract all the data correctly, but now I want to add to an object array or something like that (I started few weeks ago with Firebase + Angular2).
I wanna fill that array to load the [ng2 smart table] and if I´m thinking partially well with a properly well-formed array I will fill the table but I don´t know how. Hope anyone can help.
If you want an map (object) with key: value, you can easily do this with Array.prototype.reduce():
const map = snapshots.reduce((map, snapshot) => {
map[snapshot.key] = snapshot.val();
}, {});
Well, according to the example : https://akveo.github.io/ng2-smart-table/#/examples/using-filters ...
(You can find the source code here: https://github.com/akveo/ng2-smart-table/blob/master/src/app/pages/examples/filter/basic-example-source.component.ts)
... you have to put your data in a JSON object :
settings = {
columns: {
id: {
title: 'ID',
filter: false,
},
name: {
title: 'Full Name',
filter: false,
},
username: {
title: 'User Name',
filter: false,
},
email: {
title: 'Email',
filter: false,
}
}
};
data = [
{
id: 1,
name: 'Leanne Graham',
username: 'Bret',
email: 'Sincere#april.biz',
},
{
id: 2,
name: 'Ervin Howell',
username: 'Antonette',
email: 'Shanna#melissa.tv',
}
];
"settings" contain your columns names and "data" must match the columns from "settings".
It would be easier if we knew a bit more of your code (columns of your table + data returned by your service), but I assume something like that would work :
data = [];
this.af.database.list('/Documentos', { preserveSnapshot: true })
.subscribe(snapshots => {
snapshots.forEach(snapshot => {
data.push(
{ [snapshot.key]: snapshot.val() }
);
});
})
Please note that this will create a JSON array with only one key/val per row. We do need to know more about your data to give you a propre answer.
Ok, I found the solution with a simple Array() x)
this.af.database.list('/Documentos', { preserveSnapshot: true })
.subscribe(snapshots => {
snapshots.forEach(snapshot => {
let length = todo.documentos.push(snapshot.val()); // documentos is an array in the class
todo.source.load(todo.documentos);
});
});

Categories