How to treat JSON data in React? - javascript

I am using the following code using a flight data API:
useEffect(() => {
fetch(url, options)
.then((res) => res.json())
.then((json) => {
console.log(json);
datos.push(json)
})
.catch((err) => console.log('error' + err));
}, []);
return (
<div className='ejemplo'>
{JSON.stringify(datos)}
<div\>
)
and this is the result in the DOM:
[{"success":true,"data":{"2023-02-08":{"origin":"MAD","destination":"BCN","price":88,"airline":"UX","flight_number":7701,"departure_at":"2023-02-08T07:30:00+01:00","return_at":"2023-02-12T11:50:00+01:00","transfers":0,"expires_at":"2023-02-07T08:27:20Z"},"2023-02-09":{"origin":"MAD","destination":"BCN","price":69,"airline":"IB","flight_number":3034,"departure_at":"2023-02-09T21:15:00+01:00","return_at":"2023-02-13T18:05:00+01:00","transfers":0,"expires_at":"2023-02-07T08:27:20Z"},"2023-02-14":{"origin":"MAD","destination":"BCN","price":69,"airline":"IB","flight_number":3012,"departure_at":"2023-02-14T11:30:00+01:00","return_at":"2023-02-18T21:05:00+01:00","transfers":0,"expires_at":"2023-02-07T08:27:20Z"},"2023-02-15":{"origin":"MAD","destination":"BCN","price":69,"airline":"IB","flight_number":3034,"departure_at":"2023-02-15T21:15:00+01:00","return_at":"2023-02-19T14:30:00+01:00","transfers":0,"expires_at":"2023-02-07T08:27:20Z"},"2023-02-16":{"origin":"MAD","destination":"BCN","price":87,"airline":"IB","flight_number":3018,"departure_at":"2023-02-16T16:00:00+01:00","return_at":"2023-02-20T12:35:00+01:00","transfers":0,"expires_at":"2023-02-07T08:27:20Z"},"2023-02-17":{"origin":"MAD","destination":"BCN","price":57,"airline":"IB","flight_number":5003,"departure_at":"2023-02-17T22:20:00+01:00","return_at":"2023-02-21T20:30:00+01:00","transfers":0,"expires_at":"2023-02-07T08:27:20Z"},"2023-02-19":{"origin":"MAD","destination":"BCN","price":63,"airline":"UX","flight_number":7701,"departure_at":"2023-02-19T07:30:00+01:00","return_at":"2023-02-23T20:30:00+01:00","transfers":0,"expires_at":"2023-02-07T08:27:20Z"},"2023-02-21":{"origin":"MAD","destination":"BCN","price":69,"airline":"IB","flight_number":3012,"departure_at":"2023-02-21T11:30:00+01:00","return_at":"2023-02-25T14:30:00+01:00","transfers":0,"expires_at":"2023-02-07T08:27:20Z"},"2023-02-23":{"origin":"MAD","destination":"BCN","price":52,"airline":"UX","flight_number":7701,"departure_at":"2023-02-23T07:30:00+01:00","return_at":"2023-02-27T11:50:00+01:00","transfers":0,"expires_at":"2023-02-07T08:27:20Z"}
How can I modify my code in order to obtain just the origin, destination and price attributes? Thanks you all.
I tried to add a . plus the search attributes but no correct response was given.

You can loop your data by key and create a custom array of objects:
const [datos, setDatos] = useState([]);
useEffect(() => {
fetch(url, options)
.then((res) => res.json())
.then((json) => {
const { success, data } = json;
if (!success) console.log(error);
else {
const dataList = [];
for (const key in data) {
const { origin, destination, price } = data[key];
dataList.push({ origin, destination, price });
}
setDatos(dataList);
}
})
.catch((err) => console.log('error' + err));
}, []);
return (
<div className='ejemplo'>
{
datos.length > 0 && datos.map((d, index) => {
return <div key={index}>
<p>{d.origin}</p>
<p>{d.destination}</p>
<p>{d.price}</p>
</div>
}
}
</div>
)

You can extract only the desired attributes by using object destructuring in the .then function. Here's an example:
.then((json) => {
const {data} = json;
const flightData = Object.values(data).map(({origin, destination, price}) => ({origin, destination, price}));
datos.push(flightData);
})
In this example, Object.values(data) is used to get an array of flight data objects from the data object. Then, .map is used to extract only the origin, destination, and price properties from each object and create a new array of flight data objects with just those properties. This new array is then pushed to the datos array.

Related

Handling multiple api calls with Promis.all and manipulating the data with react-redux

I am building an app with react/ redux for managing Collection of Electronic equipment (=donations).
In the first stage I need to make 2 api calls:
the first and the second are donations data and donors data (kept as different collections in mongodb) and then combine them. This info is shown in a donation route.
The action looks like this:
const basicUrl = 'http://localhost:8000/api';
export const requestDonor_DonationData = () => getDonationData (
`${basicUrl}/donor`,
`${basicUrl}/donation`
);
and the getDonationData func looks like this:
import {
REQUEST_ENTITIES_PENDING,
REQUEST_ENTITIES_SUCCES,
REQUEST_ENTITIES_FAILED
} from './constants';
export const getDonationData = (urlDonor, urlDonation) => (dispatch) => {
dispatch ( {type: REQUEST_ENTITIES_PENDING} );
Promise.all([
fetch(urlDonor).then(res => res.json()),
fetch(urlDonation).then(res => res.json())
]).then ( ([ donorResult, donationResult]) => donorResult.data.map( (e, i) => Object.assign(e, donationResult.data[i]) ) )
.then( mergedData => dispatch({type: REQUEST_ENTITIES_SUCCES, payload: mergedData }) )
.catch(error => dispatch({type: REQUEST_ENTITIES_FAILED, payload: error}) )
}
that works fine.
In the second stage, When a donation have been peeked up, it become an equipment (not the perfect word..) which means that now it is waiting for inspection. this info is shown in a equipment route.
the equipment data contain the donationId and status (different from the donation status).
Now I want to do something similar:
make 3 api calls (getting donor, donation, & equipment data)
merging the donor whit its donation data
filtering the merged
data with the donations that have been peeked up (status='DONE')
create a new json which takes the merged data and replace the ID and
status of donation with the ID and status of the equipment.
I tried
to do that with the first approach (just with Promise.all) but found
it very confusing working with multiple ".then" ...
this is what I tried :
the action-
export const requestEquipmentData = () => getEquipmentData (
[
`${basicUrl}/donor`,
`${basicUrl}/donation`,
`${basicUrl}/equipment`
]
);
export const getEquipmentData = (urls) => (dispatch) => {
dispatch ( {type: REQUEST_ENTITIES_PENDING} );
try {
const [ donorResult, donationResult, equipmentResult ] = Promise.all(urls.map(async function(url) {
const response = await fetch(url);
return response.json();
}));
const donationInfo = donorResult.data.map( (e, i) => Object.assign(e, donationResult.data[i]) );
const filteredDonation = donationInfo.filter(item =>item.status==='DONE');
const equipment = filteredDonation.map( (donation,i) => {
let obj = donation;
obj.id = equipmentResult.data[i].id;
obj.status = equipmentResult.data[i].status;
return obj;
})
dispatch({type: REQUEST_ENTITIES_SUCCES, payload: equipment });
} catch (error) {
dispatch({type: REQUEST_ENTITIES_FAILED, payload: error})
}
}
but I am doing somethig wrong, and that is the error:
type: "REQUEST_ENTITIES_FAILED", payload: TypeError: undefined is not a function
I would appreciate any help
The result of Promise.all() is a Promise that resolves to the array of results. It is not an array itself so you cannot destructure it like this.
You can use the same .then() approach that you used in your first example:
export const getEquipmentData = (urls) => (dispatch) => {
dispatch({ type: REQUEST_ENTITIES_PENDING });
Promise.all(urls.map(async function (url) {
const response = await fetch(url);
return response.json();
})).then(([donorResult, donationResult, equipmentResult]) => {
const donationInfo = donorResult.data.map((e, i) => Object.assign(e, donationResult.data[i]));
const filteredDonation = donationInfo.filter(item => item.status === 'DONE');
const equipment = filteredDonation.map((donation, i) => {
let obj = donation;
obj.id = equipmentResult.data[i].id;
obj.status = equipmentResult.data[i].status;
return obj;
})
dispatch({ type: REQUEST_ENTITIES_SUCCES, payload: equipment });
}).catch(error) {
dispatch({ type: REQUEST_ENTITIES_FAILED, payload: error })
}
}
Or you can use async/await syntax. Checkout this question for a generally discussion on resolving an array of Promises.
export const getEquipmentData = (urls) => async (dispatch) => {
dispatch ( {type: REQUEST_ENTITIES_PENDING} );
try {
const [ donorResult, donationResult, equipmentResult ] = await Promise.all(urls.map(async function(url) {
const response = await fetch(url);
return response.json();
}));
const donationInfo = donorResult.data.map( (e, i) => Object.assign(e, donationResult.data[i]) );
const filteredDonation = donationInfo.filter(item =>item.status==='DONE');
const equipment = filteredDonation.map( (donation,i) => {
let obj = donation;
obj.id = equipmentResult.data[i].id;
obj.status = equipmentResult.data[i].status;
return obj;
})
dispatch({type: REQUEST_ENTITIES_SUCCES, payload: equipment });
} catch (error) {
dispatch({type: REQUEST_ENTITIES_FAILED, payload: error})
}
}
In my opinion your general approach here is not good. You should read the guides on Normalizing State Shape. It seems like your APIs are returning normalized data and then your are "unnormalizing" it by combining data from multiple endpoints.

How to use foreach and promise

I need to get datas with nested foreach, but I can't fill my array.
At the end of this code I would like to have an array (segId) with my datas but it is empty (because of aynschronous).
I read that I had to use Promise.all but I can't beacause my promise are nested
I'm beginner so my code is far from perfect
How can I do that ?
async function getActivities(strava, accessToken)
{
const payload = await strava.athlete.listActivities({'access_token':accessToken, 'after':'1595281514', 'per_page':'10'})
return payload;
}
async function getActivity(strava, accessToken, id)
{
const payload = await strava.activities.get({'access_token':accessToken, 'id':id, 'include_all_efforts':'true'})
return payload;
}
async function getSegment(strava, accessToken, id)
{
const payload = await strava.segments.get({'access_token':accessToken,'id':id})
return payload
}
var tableau = []
var segId = []
const activities = getActivities(strava, accessToken)
activities.then(value => {
value.forEach((element, index) => {
const activity = getActivity(strava, accessToken, element['id'])
activity.then(value => {
value['segment_efforts'].forEach((element, index) => {
const segment = getSegment(strava, accessToken, element['segment']['id'])
segment.then(value => {
segId.push(value['id'])
})
//console.log(segId)
});
});
})
}) console.log(segId)
Regards
PS : Sorry for my english ...
Something like this should work. You need to always return the inner promises to include them in your promise chain. Consider splitting the code into functions to make it more readable.
getActivities(strava, accessToken).then(activities => {
return Promise.all(activities.map(elem => {
return getActivity(strava, accessToken, elem['id']).then(activity => {
return Promise.all(activity['segment_efforts'].map(elem => {
return getSegment(strava, accessToken, elem['segment']['id']).then(segment => {
segId.push(segment['id']);
});
}));
})
}));
})
.then(_ => {
console.log(segId);
});

React/FetchAPI: How to take data from related json by id

I want to take data from JSON, next take another data from related JSON by ID and push it to my state array movies.
This is my code:
state = {
movies: []
}
componentDidMount() {
fetch('https://api.themoviedb.org/3/movie/popular?api_key=APIKEY&page=1')
.then(response => response.json())
.then(data => {
const movies = data.results;
movies.forEach(movie => this.moviePageAndGenres(movie.id, movie));
this.setState({
movies
});
})
}
moviePageAndGenres = (id, element) => {
fetch('https://api.themoviedb.org/3/movie/' + id + '?api_key=APIKEY')
.then(response => response.json())
.then(data => {
element.genres = data.genres;
element.homepage = data.homepage;
});
}
In render() I just console.log my movies to check if data inside is correct.
Output:
image
So it's correct but when I check Component Props these props are not transferred.
image
This is how I transfer props:
const movies = this.state.movies.map(movie =>
<Movie genres={movie.genres}
homepage={movie.homepage}
key={movie.id}
title={movie.title}
poster={movie.poster_path}
rating={movie.vote_average}
/>
)
I guess it's problem with multiple call of asynchronousfetch(). But i don't know how to handle with it.
The reason its not working is, you are firing multiple fetch calls which are async and setting the state immediately after it. setState will get empty movies in that case.
fetch api returns a promise and you should set your state in promise resolution handler. Modify your componentDidMount like this.
componentDidMount() {
fetch('https://api.themoviedb.org/3/movie/popular?api_key=APIKEY&page=1')
.then(response => response.json())
.then(data => {
const movies = data.results;
Promise.all(movies.map(movie => fetch(
`https://api.themoviedb.org/3/movie/${movie.id}?api_key=APIKEY`
)))
.then(resp => Promise.all( resp.map(r => r.json()) ))
.then(result => {
const movies = result.map((data, i) => {
const movie = Object.assign(movies[i], {
genres: data.genres,
homepage: data.homepage
});
return movie;
});
this.setState({
movies
});
});
})
}
You need async await in this case and it’s good to use Promise.all because you are doing fetch in forEach.
For forEach you need await Promise.all and for fetch you need await. Which mean it will wait until the forEach is completed
Change
fetch('https://api.themoviedb.org/3/movie/popular?api_key=APIKEY&page=1')
.then(response => response.json())
.then(data => {
const movies = data.results;
movies.forEach(movie => this.moviePageAndGenres(movie.id, movie));
this.setState({
movies
});
})
To
fetch('https://api.themoviedb.org/3/movie/popular?api_key=APIKEY&page=1')
.then(response => response.json())
.then(async data => {
const movies = data.results;
await Promise.all(movies.forEach(async movie => await this.moviePageAndGenres(movie.id, movie)))
this.setState({
movies
});
})
Also
Change
moviePageAndGenres = (id, element) => {
fetch('https://api.themoviedb.org/3/movie/' + id + '?api_key=APIKEY')
.then(response => response.json())
.then(data => {
element.genres = data.genres;
element.homepage = data.homepage;
});
}
To
moviePageAndGenres = async (id, element) => {
return await fetch('https://api.themoviedb.org/3/movie/' + id + '?api_key=APIKEY')
.then(response => response.json())
.then(data => {
element.genres = data.genres;
element.homepage = data.homepage;
});
}

Iterating over Object of Objects with filtering results

Hello Guys i have a problem, cause i have a task to fetch on API and then show results in specific order:
But what i've got is :
let toObject = (array) =>
array.reduce((obj,item) => {
obj[item.name.toUpperCase()] = item
return obj
}, {})
fetch('https://restcountries.eu/rest/v2/')
.then((resp) => resp.json())
.then((data) => data.filter(x => x.borders.includes('POL')))
.then(data => toObject(data))
.then(data => Object.entries(data).map(([key, value]) => ({ [key]: value }))).then(data => console.log(data))
But it isn't looking what it shoud look like. And i have no idea what to add so my code return results like the one from screenshot :(
Here is one way by which you can achieve the same. It essentially uses Array#reduce to tranform the results to the required object.
// Get the data using AJAX
$.get('https://restcountries.eu/rest/v2/')
.then(data => {
// Transform the results
let transformed = data.reduce((acc, item) => {
// In each iteration, assign the country's name as key to the accumulator
acc[item.name.toUpperCase()] = {
// Calculate neighbours using borders count
hasMoreThanFourNeighbours: item.borders.length > 4 ? "YES" : "NO",
// Area with suffix KM2
area: item.area + " KM2",
// Population
population: item.population
};
// Return the accumulator for next iteration
return acc;
}, {});
console.log(transformed);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
try using map
fetch('https://restcountries.eu/rest/v2/')
.then((resp) => resp.json())
.then((data) => data.filter(x => x.borders.includes('POL')))
.then((data) => data.reduce((obj, item) => {
obj[item.name.toUpperCase()] = {
hasMoreThanFourNeighbors: item.borders.length > 4 ? 'YES' : 'NO',
population: item.population,
area: item.area + ' KM2'
};
return obj;
}, {}))
.then(data => console.log(data))

render out API data in custom format

I'm pulling data from an API which i then push it into a variable. From my script below the console reads like this
`1001,1002`
But i want it to render out in this format
(1001),(1002)
JS
this.http.post("http://localhost/app/route_list.php", { 'username': this.username}).map(res => res.json()) .subscribe(data => {
console.log(JSON.stringify(data));
var returned=[];
for(var i=0;i<data.length;i++){
returned.push(data[i].Route_ID);
console.log('this is data returned:'+returned)
Maybe this can help you:
this.http.post('http://localhost/app/route_list.php', {
username: this.username
})
.map(res => res.json())
.subscribe(data => {
const result = data
.map(item => `(${item.Route_ID})`)
.join(',');
console.log(result);
return result;
})

Categories