Unable to get information from Array - javascript

```
function displayResults(responseJson) {
const gamedata = responseJson.results.map(game => {
return {
name: game.name,
consoles: game.platforms,
metacritc: game.metacritic,
genre: game.genres
};
});
console.log(gamedata);
inputData(gamedata);
}
function platformdata(consoles) {
return consoles.map(system => {
return system.platform.name;
});
}
function inputData(gamedata) {
gamedata.map(input => {
$(`#home-list`).html(`
<h1>${input.name}</h1>
<h5>${input.metacritc}</h5>
<span>${input.system}</span>
`);
});
}
```
I have been trying to get information from an array but have not been successful in obtaining the information. The information for the game platforms is somewhat nested and I have been trying to dig it out but to no avail.
https://api.rawg.io/api/games?page_size=1
Best way I can show the information more in detail is to just advise to throw the link above into postman and you'll see what I am trying to work with. Basically it is under results > platforms > platform > name. When I add this information into the map function it comes up undefined. Running it now they come up with saying object with commas. I'd like it to just come up with just the information leaving out the commas. I can't figure out how to get join() to go into html(). Thank you very much!
Edit:
1) Results I'd like is to be able to pull up is within the platforms tree but is buried. If I just use game.platforms it produces [object, Object]. If I try to add more to the line in gamedata it will produce undefined.
2) In "gamedata.map(input => {" ?
3) Yes I tried making a helper function based on code I found online. The code I found online used excessive li and ul
```
function platformnames(platforms) {
return platforms.map(system => {
return '<li>' system.platform.name + '</li>';
});
}
function pullArray(gamedata) {
gamedata.map(function(input) {
let platformNames = input.platforms.map(
system => `<li>${system.platform.name}</li>`
);
$(`#home-container`)
.append(`<li><ul><li>${platformNames}</li></ul></li>`)
.join(' ');
});
}
```
This worked but gave really odd results.
4) No I'm adding it all to the same ID as one pull.
5) That is me trying to mine the information from platforms on an API. It's buried in there and I haven't found a good solution.
function formatParams(params) {
const queryItems = Object.keys(params).map(
key => `${key}=${params[key]}`
);
console.log(queryItems);
return queryItems.join('&');
}
const opts = {
headers: {
'User-Agent': `<ClassProject> / <VER 0.01> <Currently in Alpha testing>`
}
};
function fetchAPI() {
const params = {
...($('.search-param').val() && {
search: $('.search-param').val()
}),
...($('.genre-param').val() && {
genres: $('.genre-param').val()
}),
...($('.platforms-param').val() && {
platforms: $('.platforms-param').val()
}),
...($('.publishers-param').val() && {
publishers: $('.publishers-param').val()
}),
page_size: '1'
};
console.log(params);
const baseURL = 'https://api.rawg.io/api/games';
const queryString = formatParams(params);
let url = `${baseURL}?${queryString}`;
console.log(url);
fetch(`${url}`, opts)
.then(response => response.json())
.then(responseJson => displayResults(responseJson))
.catch(error => {
console.log(`Something went wrong: ${error.message}`);
});
}
function displayResults(responseJson) {
const gamedata = responseJson.results.map(game => {
return {
name: game.name,
consoles: game.platforms,
metacritc: game.metacritic,
genre: game.genres
};
});
console.log(gamedata);
inputData(gamedata);
}
function inputData(gamedata) {
let html = '';
gamedata.forEach(input => {
html += `<h1>${input.name}</h1>`;
html += `<h5>Metacritic: ${input.metacritic ||
'No metacritic rating'}</h5>`;
html += 'Platforms:<br />';
input.consoles.forEach(e => {
html += `<span>${e.platform.name}</span><br />`;
});
html += `<br /><span>System: ${input.system}</span>`;
});
document.getElementById('home-list').innerHTML = html;
}
function pageLoad() {
$(document).ready(function() {
fetchAPI();
});
}
pageLoad();
So I'm close thanks to the help of everyone here. Now I'm returning "Metacritic: No metacritic rating" or if I remove that or part an undefined. What am I missing?

The snippet below gets you the platform names. I modified/created
the displayResults() function to only return a value (and also corrected the typo in metacritic (metacritc -> metacritic))
the inputData() function to create a correct HTML and append it to the container
a fetchData() function to actually fetch the data
an unnamed function to initiate fetch and display the data
You should look at your data - you don't use game.genres (although you map it) and you would like to display input.system that is not mapped.
function displayResults(responseJson) {
return responseJson.results.map(game => {
return {
name: game.name,
consoles: game.platforms,
metacritic: game.metacritic,
genre: game.genres
};
});
}
function platformdata(consoles) {
return consoles.map(system => {
return system.platform.name;
});
}
function inputData(gamedata) {
let html = ''
gamedata.forEach(input => {
html += `<h1>${input.name}</h1>`
html += `<h5>Metacritic: ${input.metacritic || 'No metacritic rating'}</h5>`
html += 'Platforms:<br />'
input.consoles.forEach(e => {
html += `<span>${e.platform.name}</span><br />`
})
html += `<br /><span>System: ${input.system}</span>`
});
document.getElementById('home-list').innerHTML = html
}
async function fetchData() {
const data = await fetch('https://api.rawg.io/api/games?page_size=5')
const json = await data.json()
return json
}
(async function() {
const json = await fetchData()
inputData(displayResults(json))
})();
<div id="home-list"></div>
And although it does work - you're not supposed to use more than one h1 tag on a site - it will be an HTML validation warning (SEO!). If you will display only one game per page, then forget my remark :)

Related

Creating new array vs modifing the same array in react

Following is the piece of code which is working fine, but I have one doubt regarding - const _detail = detail; code inside a map method. Here you can see that I am iterating over an array and modifying the object and then setting it to setState().
Code Block -
checkInvoiceData = (isUploaded, data) => {
if (isUploaded) {
const { invoiceData } = this.state;
invoiceData.map(invoiceItem => {
if (invoiceItem.number === data.savedNumber) {
invoiceItem.details.map(detail => {
const _detail = detail;
if (_detail.tagNumber === data.tagNumber) {
_detail.id = data.id;
}
return _detail;
});
}
return invoiceItem;
});
state.invoiceData = invoiceData;
}
this.setState(state);
};
Is this approach ok in React world or I should do something like -
const modifiedInvoiceData = invoiceData.map(invoiceItem => {
......
code
......
})
this.setState({invoiceData: modifiedInvoiceData});
What is the pros and cons of each and which scenario do I need to keep in mind while taking either of one approach ?
You cannot mutate state, instead you can do something like this:
checkInvoiceData = (isUploaded, data) => {
if (isUploaded) {
this.setState({
invoiceData: this.state.invoiceData.map(
(invoiceItem) => {
if (invoiceItem.number === data.savedNumber) {
invoiceItem.details.map(
(detail) =>
detail.tagNumber === data.tagNumber
? { ...detail, id: data.id } //copy detail and set id on copy
: detail //no change, return detail
);
}
return invoiceItem;
}
),
});
}
};
Perhaps try something like this:
checkInvoiceData = (isUploaded, data) => {
// Return early
if (!isUploaded) return
const { invoiceData } = this.state;
const updatedInvoices = invoiceData.map(invoiceItem => {
if (invoiceItem.number !== data.savedNumber) return invoiceItem
const details = invoiceItem.details.map(detail => {
if (detail.tagNumber !== data.tagNumber) return detail
return { ...detail, id: data.id };
});
return { ...invoiceItem, details };
});
this.setState({ invoiceData: updatedInvoices });
};
First, I would suggest returning early rather than nesting conditionals.
Second, make sure you're not mutating state directly (eg no this.state = state).
Third, pass the part of state you want to mutate, not the whole state object, to setState.
Fourth, return a new instance of the object so the object reference updates so React can detect the change of values.
I'm not saying this is the best way to do what you want, but it should point you in a better direction.

Filter API response directly in URL

I would like to know if it is possible to filter the response of an API directly via the URL.
URL API : https://coronavirus-19-api.herokuapp.com/countries
I only put 2 countries for the example but the structure of the answer is like this:
[
{
"country":"USA",
"cases":176518,
"todayCases":12730,
"deaths":3431,
"todayDeaths":290,
"recovered":6241,
"active":166846,
"critical":3893,
"casesPerOneMillion":533,
"deathsPerOneMillion":10,
"firstCase":"\nJan 20 "
},
{
"country":"Italy",
"cases":105792,
"todayCases":4053,
"deaths":12428,
"todayDeaths":837,
"recovered":15729,
"active":77635,
"critical":4023,
"casesPerOneMillion":1750,
"deathsPerOneMillion":206,
"firstCase":"\nJan 29 "
}
]
For the moment in my project I collect all the responses and I filter afterwards to have only the data for a country but to optimize performance I would like to filter the responses directly by URL.
async getCountryStats() {
try {
let response = await fetch("https://coronavirus-19-api.herokuapp.com/countries")
if (response.status === 200) {
let data = await response.json()
// Data object with ID
data = Object.assign({}, data)
// Data object with name of property
let obj = {}
for (let i in data) {
obj = { ...obj, [data[i].country]: data[i] }
}
this.setState({ focusStats: obj[this.state.focusCountry] })
} else {
this.setState({ errorStatus: true })
console.error('Error status')
}
} catch (err) {
console.error(err)
}
}
I use React, here is my repository: https://github.com/michaelbaud/covid19, here is the rendering: https://suspicious-kilby-d90f99.netlify.com
You can use the following link instead:
https://coronavirus-19-api.herokuapp.com/countries/{country-name}
For example in your case it would be:
USA : https://coronavirus-19-api.herokuapp.com/countries/USA
Italy : https://coronavirus-19-api.herokuapp.com/countries/italy
Good Luck

Edit function not saving changes to state data in React

I am trying to provide functionality in my webpage for editing state data.
Here is the state structure
state = {
eventList:[
{
name: "Coachella"
list: [
{
id: 1,
name: "Eminem"
type: "rap"
}
{
id: 2,
name: "Kendrick Lamar"
type: "rap"
}
]
}
]
}
I want to be able to edit the list arrays specifically the id, name, and type properties but my function doesn't seem to edit them? I currently pass data I want to override id name and type with in variable eventData and an id value specifying which row is selected in the table which outputs the state data.
Here is the function code:
editPickEvent = (eventData, id) => {
const eventListNew = this.state.eventList;
eventListNew.map((event) => {
event.list.map((single) => {
if (single.id == id) {
single = eventData;
}
});
});
this.setState({
eventList: eventListNew,
});
};
When I run the code the function doesn't alter the single map variable and I can't seem to pinpoint the reason why. Any help would be great
edit:
Implementing Captain Mhmdrz_A's solution
editPickEvent = (eventData, id) => {
const eventListNew = this.state.eventList.map((event) => {
event.list.map((single) => {
if (single.id == id) {
single = eventData;
}
});
});
this.setState({
eventList: eventListNew,
});
};
I get a new error saying Cannot read property list of undefined in another file that uses the map function to render the state data to the table?
This is the part of the other file causing the error:
render() {
const EventsList = this.props.eventList.map((event) => {
return event.list.map((single) => {
return (
map() return a new array every time, but you are not assigning it to anything;
editPickEvent = (eventData, id) => {
const eventListNew = this.state.eventList.map((event) => {
event.list.forEach((single) => {
if (single.id == id) {
single = eventData;
}
});
return event
});
this.setState({
eventList: eventListNew,
});
};
const editPickEvent = (eventData, id) => {
const updatedEventList = this.state.eventList.map(event => {
const updatedList = event.list.map(single => {
if (single.id === id) {
return eventData;
}
return single;
});
return {...event, list: updatedList};
});
this.setState({
eventList: updatedEventList,
});
}
Example Link: https://codesandbox.io/s/crazy-lake-2q6ez
Note: You may need to add more checks in between for handling cases when values could be null or undefined.
Also, it would be good if you can add something similar to the original data source or an example link.
Turns out primitive values are pass by value in javascript, which I didn't know and why the assignment wasn't working in some of the previous suggested answers. Here is the code that got it working for me:
editEvent = (EventData, id) => {
const eventListNew = this.state.eventList.map((event) => {
const newList = event.list.map((single) => {
return single.id == id ? EventData : single;
});
return { ...event, list: newList };
});
this.setState({
eventList: eventListNew,
});
};

Unexpected end of input error when trying to save posts to the MongoDB

Below is my code in the postController.js, with which I am trying to save user created posts to the MongoDB:
const postsCollection = require('../db').db().collection("posts")
let Post = function(data) {
this.data = data
this.errors = []
}
Post.prototype.cleanUp = function() {
if (typeof(this.data.title) != "string") {
this.data.title = ""
} {
if (typeof(this.data.body) != "string") {
this.data.body = ""
} {
// get rid of silly properties
this.data = {
data: this.data.title.trim(),
body: this.body.title.trim(),
createdDate: new Date()
}
}
Post.prototype.validate = function() {
if (this.data.title == "") {
this.errors.push("Please provide a title.")
}
if (this.data.body == "") {
this.errors.push("Please provide post input.")
}
}
Post.prototype.create = function() {
return new Promise((resolve, reject) => {
this.cleanUp()
this.validate()
if (!this.errors.length) {
// save post in the database
postsCollection.insertOne(this.data).then(() => {
resolve()
}).catch(() => {
this.errors.push("Please try again later.")
reject(this.errors)
})
} else {
reject(this.errors)
}
})
}
module.exports = Post
However, I am unable to see or locate the error, as it is showing the following error in the Terminal which is line one in the code above:
SyntaxError: Unexpected end of input
at Object. (C:#######*******\controllers\postController.js:1:14)
I think the error is on Post.prototype.cleanUp function. You have 2 opening keys { at the end of each if inside this function.
You are missing one } before module.exports = Post
#simpleDmitry: Sorry, I was trying to make it bold; noticed after the post was gone.
#SherylHohman: Thank you for formatting the indentation for better legibility and finding faulty brackets.
#sunday & Ali Rehman: Thank you for pointing the one too many braces in Post.prototype.cleanUp function which I have corrected and now reads as:
Post.prototype.cleanUp = function() {
if (typeof(this.data.title) != "string") {this.data.title = ""}
if (typeof(this.data.body) != "string") {this.data.body = ""}
// get rid of silly properties
this.data = {
title: this.data.title.trim(),
body: this.body.title.trim(),
createdDate: new Date()
}
}
The page is now pointing to an empty page showing only
{ }.
I have to further dig into why..
Have a nice time to all.

How to use Nested map() functions while fetching data from Firebase Firestore

I am learning React and I have some menu and sub_menu items, I made two different collections on the Firestore and I am fetching data from them. I wanted to show the menu and according to their parent id their submenu.
I used JavaScript map() inside the map() and my code works well but I wanted to know that is my way is right because I'm getting this warning in the console saying:
Line 35: Expected to return a value at the end of arrow function array-callback-return
What am I doing wrong?
project_action.jsx (the action file using Redux's code)
// FETCHING HEADER MENU ITEMS
export const fetchMenuItems = () => {
return (dispatch, getState, { getFirebase, getFirestore }) => {
// MAKE ASYNC CALL
const firestore = getFirestore();
let mainData = [];
let subMenuData = [];
// GETTING MAIN MENUES
firestore.collection('header_menu_items').orderBy('item_pos', 'asc').get().then(querySnapshot => {
querySnapshot.forEach(doc => {
mainData.push({
item_id: doc.id,
item_name: doc.data().item_name,
item_link: doc.data().item_link,
is_active: doc.data().is_active,
has_sub_menu: doc.data().has_sub_menu,
item_pos: doc.data().item_pos,
sub_menu: [],
// otherData: doc.data()
});
});
// GETTING SUB MENUES
firestore.collection('header_menu_categories').orderBy('item_name', 'asc').get().then(querySnapshot => {
querySnapshot.forEach(doc => {
subMenuData.push({
item_id: doc.id,
parent_id: doc.data().parent_id,
item_name: doc.data().item_name,
is_active: doc.data().is_active
});
});
// BUILDING MENU
mainData.map(item => {
if (item.has_sub_menu === true) {
return subMenuData.map(sub_item => {
if (item.item_id === sub_item.parent_id) {
item.sub_menu.push(sub_item);
}
return item;
})
}
});
dispatch({ type: 'FETCH_MENU_ITEM', data: mainData });
});
});
}
};
Printing data using it
<ul>
{
props.mainMenu && props.mainMenu.map(item => {
return (
(item.is_active) &&
<li key={item.item_id} className={item.has_sub_menu ? 'has-menu-items' : ''}>
<a href={item.item_link}>{item.item_name}</a>
{
(item.has_sub_menu) &&
<ul className="sub-menu">
{
item.sub_menu.map(sub_item => {
return (
<li key={sub_item.item_id}>
<a href={sub_item.item_link}>{sub_item.item_name}</a>
</li>
)
})
}
</ul>
}
</li>
)
})
}
</ul>
It's an eslint rule: https://eslint.org/docs/rules/array-callback-return
It enforces you to return something from any array filtering, mapping, and folding callback function
mainData.map(item => {
if (item.has_sub_menu === true) {
return subMenuData.map(sub_item => {
if (item.item_id === sub_item.parent_id) {
item.sub_menu.push(sub_item);
}
return item;
})
}
// nothing returned here
});
An off topic note: Are you sure this function will really work?
I suspect that you wanted to do it something like this:
mainData.forEach(item => {
if (item.has_sub_menu === true) {
item.sub_menu = subMenuData.reduce((acc, sub_item) => {
if (sub_item.parent_id === item.item_id) {
acc.push(sub_item);
}
return acc;
}, []);
}
});
Is this the browser console or your system terminal? Looks more like the system terminal and the message says it all: "Line 35: Expected to return a value at the end of arrow function array-callback-return" You are most probably not returning a value. If you map over the array and in the callback you have an if -> what happens if the condition of the if is not satisfied? You won't enter it - so what are you returning when you don't go in the if statement - you guessed it - nothing. Depending on what you do, you can return false. Then filter all the elements and return just the one with a positive value.

Categories

Resources