fetching data and adding title to Json object - javascript

I would like to add title to my JSON object, the structure I wish to achieve is:
{
"posts": [
{
"title": "put title here",
"upvotes": 1234,
"score": 1000,
"num_comments": 100,
"created": "16.05.2019 12:12",
},
]
}
I was able to fetch data and put it into array of 26 elements, everything is fine but I wish to somehow add this "posts:" to be above whole rest, here is my code:
fetch("https://www.reddit.com/r/funny.json")
.then(resp => resp.json()
.then(async res => {
let posts = await res.data.children.map(el => {
let title = el.data.title;
let upvote = el.data.ups;
let score = el.data.score;
let comments = el.data.num_comments;
let created = el.data.created;
const allPosts = {title, upvote, score, comments, created}
postList.push(allPosts)
return postList
})
console.log(posts);
return posts
})

You might need to create the object like below
{propertyName:value}
const allPosts = {title:title,upvote: upvote,score: score,comments: comments, created:created}
postList.push(allPosts)

fetch("https://www.reddit.com/r/funny.json")
.then(resp => resp.json())
.then(async res => {
console.log(res);
let posts = await res.data.children.map(el => {
let title = el.data.title;
let upvote = el.data.ups;
let score = el.data.score;
let comments = el.data.num_comments;
let created = el.data.created;
const allPosts = { title, upvote, score, comments, created };
let postList = [];
postList.push(allPosts);
return postList;
});
console.log({"posts": posts});
return {"posts": posts};
});

You can try out the following code.
fetch("https://www.reddit.com/r/funny.json")
.then(resp => resp.json())
.then(res => ({
posts: res.data.children.map(el => ({
title: el.data.title,
upvote: el.data.upvote,
score: el.data.score,
comments: el.data.num_comments,
created: el.data.created
}))
}))
.then(posts => {
console.log(posts);
});

You can do it in this way:
fetch("https://www.reddit.com/r/funny.json")
.then(resp => resp.json()
.then(async res => {
let posts = await res.data.children.map(el => {
return {
title: el.data.title,
upvote: el.data.ups,
score: el.data.score,
comments: el.data.num_comments,
created: el.data.created
}
})
const postObject = { posts }
console.log(postObject);
return postObject
})
Map function return value, in this way you get an object with key (posts) and values (an object with details).

Related

Compare two arrays in react and filter one based on matches of name property

I'm trying to filter a players array by comparing the names property to a roster array and rendering the matches from the players array.
When a user selects a year the getCollegeRoster() function returns players who have a year property matching the selection. From there I'm trying to filter and display the .name matches from the players array, but it seems i'm unable to update playerStateValue. I'm using recoil to manage state. Any insight would be much appreciated.
const getCollegeRoster = async () => {
setLoading(true);
try {
const playersQuery = query(
collection(firestore, `colleges/${communityData.id}/rosters`),
where("year", "==", selection),
);
const playerDocs = await getDocs(playersQuery);
const collegeRoster = playerDocs.docs.map((doc) => ({ id: doc.id, ...doc.data() }));
setRoster(collegeRoster as unknown as Roster[]);
console.log('collegePlayers', roster);
} catch (error: any) {
console.log("getCollegePlayers error", error.message);
}
setLoading(false);
};
const onSelection = (newSelection: any) => {
setSelection(newSelection);
console.log('newselect', newSelection)
getCollegeRoster();
const filteredArray = [...playerStateValue.players].filter((el) => ({
name: el.name,
match: [...roster].some((f) => f.name === el.name)
})
);
setPlayerStateValue((prev) => ({
...prev,
players: filteredArray as Player[],
playersCache: {
...prev.playersCache,
[communityData?.id!]: filteredArray as Player[],
},
playerUpdateRequired: false,
}));
} ```
also tried adding setplayerstatevalue into the getcollegeroster function:
onst getCollegeRoster = async () => {
console.log("WE ARE GETTING Players!!!");
console.log('communitydata', communityData);
setLoading(true);
try {
const playersQuery = query(
collection(firestore, `colleges/${communityData.id}/rosters`),
where("year", "==", selection),
);
const playerDocs = await getDocs(playersQuery);
const collegeRoster = playerDocs.docs.map((doc) => ({ id: doc.id, ...doc.data() }));
setRoster(collegeRoster as unknown as Roster[]);
console.log('collegePlayers', roster);
const filteredArray = [...playerStateValue.players].filter((el) => ({
name: el.name,
match: [...roster].some((f) => f.name === el.name)
})
);
setPlayerStateValue((prev) => ({
...prev,
players: filteredArray as Player[],
playersCache: {
...prev.playersCache,
[communityData?.id!]: filteredArray as Player[],
},
playerUpdateRequired: false,
}));
} catch (error: any) {
console.log("getCollegePlayers error", error.message);
}
setLoading(false);
};
{playerStateValue.players.map((player: Player, index) => (
<PlayerItem
key={player.id}
player={player}
// postIdx={index}
onPlayerVote={onPlayerVote}
userVoteValue={
playerStateValue.playerVotes.find((item) => item.playerId === player.id)
?.voteValue
}
onSelectPlayer={onSelectPlayer}
/>
I believe I found the problem. getCollegeRoster() is an async function which means it is being executed asynchronous.
That function is responsible for updating roster and you are using roster inside your filtering while it is being asynchronously updated.
There is no guarantee that roster is updated by the time you filter your array.
You have two ways of solving this. Either you execute setPlayerStateValue() inside of getCollegeRoster() or you wait for getCollegeRoster() to be done by preappending it with await and your onSelection function with async:
const onSelection = async (newSelection: any) => {
setSelection(newSelection);
console.log("newselect", newSelection);
await getCollegeRoster();
const filteredArray = [...playerStateValue.players].filter((el) => ({
name: el.name,
match: [...roster].some((f) => f.name === el.name),
}));
...
Edit:
Another scenario I can think of is that playerStateValue might be the culprit. In React setState works asynchronous as well, if you access playerStateValue via curr/prev-callback, it is guaranteed that you are accessing the values of the current state object while updating the state. Try this use of setPlayerStateValue:
setPlayerStateValue((prev) => {
const filteredArray = prev.players.filter((el) => ({
name: el.name,
match: [...roster].some((f) => f.name === el.name),
})) as Player[];
return {
...prev,
players: filteredArray,
playersCache: {
...prev.playersCache,
[communityData?.id!]: filteredArray,
},
playerUpdateRequired: false,
};
});

How can I loop through the keys of an object and set values equal to a destructured variable?

I am trying to simplify and shorten my current code. My code is working fine. This is what I have:
app.patch('/listings/:id', (req, res) => {
const { id } = req.params;
console.log(req.body)
const {
leaser, propertyType, pricePerDay,
currency, city, street, description
} = req.body;
const foundListing = listings.find(l => l.id === id);
foundListing.leaser = leaser;
foundListing.pricePerDay = pricePerDay;
foundListing.propertyType = propertyType;
foundListing.currency = currency;
foundListing.city = city;
foundListing.street = street;
foundListing.description = description;
res.redirect('/listings');
})
How can I make the repetitive last half of the code shorter? There is probably a way in which I can write only one line
You can use the spread operator.
app.patch('/listings/:id', (req, res) => {
const { id } = req.params;
console.log(req.body)
const {
leaser, propertyType, pricePerDay,
currency, city, street, description
} = req.body;
const foundListing = {
...listings.find(l => l.id === id),
...req.body
};
res.redirect('/listings');
})
This isn't much shorter, but that's partly because we have it all in one place. If you had a util function to copy these properties, you cna just call it when needed.
Partially applying the functions is entirely down to personal preference 🤷‍♂️
const listings = [
{ id: 1, name: "a" },
{ id: 2, name: "b" },
];
const copyProperties = ps => from => to => {
ps.forEach(p => {
to[p] = from[p];
});
}
const applyRequestToListing = req => copyProperties([
"leaser",
"pricePerDay",
"propertyType",
"currency",
"city",
"street",
"description",
])(req);
const handleRequest = (req) => {
const foundListing = listings.find(l => {
return l.id === req.params.id
});
applyRequestToListing(req.body)(foundListing);
};
handleRequest({
params: {
id: 2,
},
body: {
leaser: "leaserIn",
pricePerDay: "pricePerDayIn",
propertyType: "propertyTypeIn",
currency: "currencyIn",
city: "cityIn",
street: "streetIn",
description: "descriptionIn",
},
});
console.log(listings);
If all the foundListing properties are going to be modified, then you could loop through the properties to make changes, like so:
app.patch('/listings/:id', (req, res) => {
const { id } = req.params,
foundListing = listings.find(l => l.id === id);
for(const key in foundListing){
foundListing[key] = req.body[key]
}
res.redirect('/listings');
})
If you need to specify which properties are going to be modified then you can list them in an array and loop through them to make changes, like so:
app.patch('/listings/:id', (req, res) => {
const { id } = req.params,
foundListing = listings.find(l => l.id === id),
keys = [
'leaser',
'propertyType',
'pricePerDay',
'currency',
'city',
'street',
'description'
];
keys.forEach(key => { foundListing[key] = req.body[key] })
res.redirect('/listings');
})
I would use the following approach.
app.patch('/listings/:id', (req, res) => {
const { id } = req.params;
const foundListing = listings.find(l => l.id === id);
Object.keys(foundListing).forEach(key => {
foundListing[key] = req.body[key]
})
res.redirect('/listings');
})
You can use Object.assign() in combination with the spread operator to copy the properties of req.body to the foundListing like this:
const foundListing = listings.find(l => l.id === id);
Object.assign(foundListing, ...req.body)
Or even shorter (as you probably don't need the foundListing variable later):
Object.assign(listings.find(l => l.id === id), ...req.body);
Suddenly your code becomes really short:
app.patch('/listings/:id', (req, res) => {
Object.assign(listings.find(l => l.id === req.params.id), ...req.body);
res.redirect('/listings');
})
If you want to copy only certain properties of req.body, you could do it like this:
const foundListing = listings.find(l => l.id === id);
["leaser", "pricePerDay", "propertyType", "currency", "city", "street", "description"].forEach((e) => {
foundListing[e] = req.body[e];
});

How to consume one API endpoint which requires data from another API endpoint (dog.ceo)?

how to consume from one api with another api.
var url_1 = 'https://dog.ceo/api/breeds/list/all';
fetch(url_1)
.then( response => response.json())
.then(data => {
const breeds = data.message;
var arr = [];
for (var b in breeds) {
arr.push({
breed : b,
subBreeds : [
breeds[b][0]
],
images : [{
url: ''
}]
})
}
I also have this other api, from where I extract the images of each breed of dog, but here you need the variable that would be the name of the dog's breed.
var url_2 = 'https://dog.ceo/api/breed/{breed_name}/images';
fetch(url_2)
.then( response => response.json())
.then(data => {
const images = data.message;
var arr_images = [];
for (var i in images) {
arr_images.push({
images : [{
url: images[i]
}]
})
}
So what I don't know, how can I join to send the name of the dog's breed to the second api to consume it?
And how can I join the arrangement of the images with the arrangement above?
it should be something like this
{ "breed": "Hound",
"subBreeds": [
"subBreedA",
"subBreedB",
"subBreedC"
],
"images":[
{"url":"http://some.url.com"},
{"url":"http://some.other.url"}
]
}
I hope I have been clear, thanks for your help, I will be very grateful.
I would split it up into separate functions so that you can focus on one part at a time. Then, combine them to get all of the data that you want. In this way, you can also re-use each function in case you want to use the data in a different way:
TS Playground
// dog.ceo API
async function fetchDogApiResult (apiPath) {
const response = await fetch(`https://dog.ceo/api/${apiPath}`);
if (!response.ok) throw new Error(`Response not OK (${response.status})`);
const data = await response.json();
if (data.status !== 'success') throw new Error('Response not successful');
return data.message;
}
async function fetchBreeds () {
return fetchDogApiResult('breeds/list/all');
}
async function fetchSubBreeds (breed) {
return fetchDogApiResult(`breed/${breed}/list`);
}
async function fetchImages (breed, subBreed) {
return fetchDogApiResult(`breed/${breed}${subBreed ? `/${subBreed}` : ''}/images`);
}
async function fetchDogData () {
const breeds = await fetchBreeds();
return Promise.all(Object.entries(breeds).map(async ([breed, subBreeds]) => ({
breed,
subBreeds,
images: (await fetchImages(breed)).map(url => ({url})),
})));
}
(async () => {
const dogData = await fetchDogData();
console.log(JSON.stringify(dogData));
})();
You can use async/await for call second api in second then of first api, after you get data from second api, you can use for loop for them. like this
var url_1 = 'https://dog.ceo/api/breeds/list/all';
fetch(url_1)
.then( response => response.json())
.then(async data => {
const breeds = data.message;
const resUrl2 = await fetch(url_2)
const dataUrl2 = await resUrl2.json()
var arr = [];
for (var b in breeds) {
arr.push({
breed : b,
subBreeds : [
breeds[b][0]
],
images : [{
url: ''
}]
})
}
const images = dataUrl2.message;
var arr_images = [];
for (var i in images) {
arr_images.push({
images : [{
url: images[i]
}]
})
}
})

Component not updating even after state is changed

I have this friends component where I search the database for the user's friends (stored in a string separated by commas) and display them in a scrollable div. You can also add a new friend. Everything is working fine in the database and the backend. However, the component isn't being re-rendered when the state changes. It's dependent on 'allFriends' so it should re-render when that gets updated. Do you guys have any idea what's going on here? I'm guessing it has something to do with asynchronicity but I can't seem to pinpoint it. I added a comment for each code block to try to make it a little easier to read.
import React, { useState, useEffect } from 'react';
import SingleFriend from './SingleFriend';
import './friends.css';
const Friends = ({username}) => {
const [allFriends, setAllFriends] = useState([]);
const [unsortedFriends, setUnsortedFriends] = useState([]);
const [friendFilter, setFriendFilter] = useState('');
const [friendSearch, setFriendSearch] = useState('');
//-----------------------------------------------------------------------------------
// Start fetching friends on component mount
//-----------------------------------------------------------------------------------
useEffect(() => {
fetchFriends();
}, [])
//-----------------------------------------------------------------------------------
// Sort the friends when fetching has finished/unsortedFriends has updated
//-----------------------------------------------------------------------------------
useEffect(() => {
const onlineFriends = [];
const offlineFriends = [];
unsortedFriends.forEach(f => {
if (f.status === 'online') {
onlineFriends.push(f)
} else {
offlineFriends.push(f)
}
})
setAllFriends(onlineFriends.concat(offlineFriends));
},[unsortedFriends])
//-----------------------------------------------------------------------------------
// Get the string of friends that is stored in the database for the user
// Convert to array of friends
// Pass the array to 'fetchFriendData()'
//-----------------------------------------------------------------------------------
const fetchFriends = () => {
let allFriendNames = [];
fetch(`http://localhost:8000/getFriends?username=${username}`)
.then(res => res.json())
.then(friends => {
if (friends !== null && friends !== '') {
allFriendNames = friends.split(',');
fetchFriendData(allFriendNames);
}
})
}
//-----------------------------------------------------------------------------------
// Search the database for each of the user's friends
// Return those users, and add their username & online status to a temporary array
// Assign that array to the unsortedFriends useState hook
//-----------------------------------------------------------------------------------
const fetchFriendData = (allFriendNames) => {
let allF = [];
for (let friend of allFriendNames) {
fetch(`http://localhost:8000/findFriend?username=${friend}`)
.then(res => res.json())
.then(user => {
if (user.socketid) {
allF.push({name: user.username, status: 'online'})
} else {
allF.push({name: user.username, status: 'offline'})
}
})
.catch(err => console.log(err))
}
document.querySelector('.addFriendInput').value = '';
setUnsortedFriends(allF);
}
//-----------------------------------------------------------------------------------
// Called on button press to add friend
//-----------------------------------------------------------------------------------
const addFriend = () => {
let friendList = '';
let friendArray = [];
//-----------------------------------------------------------------------------------
// Search the database to check if the name that was
// entered matches any users in the databse
//-----------------------------------------------------------------------------------
fetch(`http://localhost:8000/findFriend?username=${friendSearch}`)
.then(res => res.json())
.then(user => {
if (user.username) {
//-----------------------------------------------------------------------------------
// If friend exists, grab the user's friend list string
// Make a temporary string and array with the new friend
//-----------------------------------------------------------------------------------
fetch(`http://localhost:8000/getFriends?username=${username}`)
.then(res => res.json())
.then(friends => {
if (friends !== null && friends !== '') {
friendArray = friends.split(',');
if (friendArray.includes(friendSearch)) {
throw new Error('Problem getting friend')
} else {
friendArray.push(friendSearch);
friendList = friends.concat(`,${friendSearch}`);
}
} else {
friendList = friendSearch;
friendArray = [friends];
}
//-----------------------------------------------------------------------------------
// Update the user's friend list with the new friends string
// Pass the updated friends array to 'fetchFriendData'
//-----------------------------------------------------------------------------------
fetch('http://localhost:8000/addFriend', {
method: 'put',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
username: username,
friendlist: friendList
})
})
.then(fetchFriendData(friendArray))
.catch(err => console.log(err))
})
.catch(err => console.log(err))
}
})
.catch(err => console.log(err))
}
return (
<div className='friendsContainer'>
<div className='friendsSection'>
<h2>Friends</h2>
<input onChange={(e) => setFriendFilter(e.target.value)} type='text' placeholder='Enter a username'/>
<div className='friendsListContainer'>
{
allFriends.length
?
<div className='friendsList'>
{
//-----------------------------------------------------------------------------------
// Map through the user's friends
// Return a single friend div w/ their username and status
//-----------------------------------------------------------------------------------
allFriends.map(f => {
if (f.name.toLowerCase().includes(friendFilter.toLowerCase())) {
return <SingleFriend key={f.name} name={f.name} status={f.status}/>
} else return null
})
}
</div>
: <h4 className='noFriends'>No friends have been added</h4>
}
</div>
<div className='addFriend'>
<h3 className='addFriendText' >Add a friend</h3>
<input className='addFriendInput' onChange={(e) => setFriendSearch(e.target.value)} type='text' placeholder='Enter a username'/>
<button onClick={addFriend} >Add</button>
</div>
</div>
</div>
)
}
export default Friends;
you need wait fetch response data
const fetchFriendData = async (allFriendNames) => {
let allF = [];
for (let friend of allFriendNames) {
const response = await fetch(`http://localhost:8000/findFriend?username=${friend}`)
const user = await response.json()
if (user.socketid) {
allF.push({name: user.username, status: 'online'})
} else {
allF.push({name: user.username, status: 'offline'})
}
}
document.querySelector('.addFriendInput').value = '';
setUnsortedFriends(allF);
}

How to use promise and loop over mongoose collection

I'm making chat inside my website. To store data I use Chat, User, Messages collections.
I want results to be in Array containing:
[{
username (another one, not me)
last update
last message
}]
In Chat model I have only chatid and array of two members, so I need to loop through User collection to get user name using user id from it. I want to save in array all names (in future I would also like to loop through messages to get latest messages for each chatid). Issue is that when I return chatsList it is empty. I think I need somehow to use Promise, but I'm not completely sure how it should work.
Chat.find({ members: userId })
.then(chats => {
let chatsList = [];
chats.forEach((chat, i) => {
let guestId = chat.members[1 - chat.members.indexOf(userId)];
User.findOne({ _id: guestId })
.then(guest => {
let chatObj = {};
name = guest.name;
chatsList.push(name);
console.log("chatsList", chatsList)
})
.catch(err => console.log("guest err =>", err))
})
return res.json(chatsList)
})
.catch(err => {
errors.books = "There are no chats for this user";
res.status(400).json(errors);
})
Indeed, Promise.all is what you are looking for:
Chat.find({ members: userId })
.then(chats => {
let userPromises = [];
chats.forEach((chat, i) => {
let guestId = chat.members[1 - chat.members.indexOf(userId)];
userPromises.push(User.findOne({ _id: guestId }));
});
return Promise.all(userPromises).then(guests => {
let chatsList = [];
guests.forEach(guest => {
chatsList.push(guest.name);
});
return res.json(chatsList);
});
});
});
although it would probably be better to do a single call to DB with a list of ids ($in query). Something like this:
Chat.find({ members: userId })
.then(chats => {
let ids = [];
chats.forEach((chat, i) => {
let guestId = chat.members[1 - chat.members.indexOf(userId)];
ids.push(guestId);
});
return User.find({_id: {$in: ids}}).then(guests => {
let chatsList = [];
guests.forEach(guest => {
chatsList.push(guest.name);
});
return res.json(chatsList);
});
});
});
You may want to additionally validate if every id had a corresponding guest.
You are running into concurrency issues. For example, running chats.forEach, and inside forEach running User.findOne().then: The return statement is already executed before the User.findOne() promise has resolved. That's why your list is empty.
You could get more readable and working code by using async/await:
async function getChatList() {
const chats = await Chat.find({members: userId});
const chatsList = [];
for (const chat of chats) {
let guestId = chat.members[1 - chat.members.indexOf(userId)];
const guest = await User.findOne({_id: guestId});
chatsList.push(guest.name);
}
return chatsList;
}
Then the code to actually send the chat list back to the user:
try {
return res.json(await getChatList());
} catch (err) {
// handle errors;
}
You can try this:
Chat.find({ members: userId }).then(chats => {
let guestHashMap = {};
chats.forEach(chat => {
let guestId = chat.members.filter(id => id != userId)[0];
// depending on if your ID is of type ObjectId('asdada')
// change it to guestHashMap[guestId.toString()] = true;
guestHashMap[guestId] = true;
})
return Promise.all(
// it is going to return unique guests
Object.keys(guestHashMap)
.map(guestId => {
// depending on if your ID is of type ObjectId('asdada')
// change it to User.findOne({ _id: guestHashMap[guestId] })
return User.findOne({ _id: guestId })
}))
})
.then(chats => {
console.log(chats.map(chat => chat.name))
res.json(chats.map(chat => chat.name))
})
.catch(err => {
errors.books = "There are no chats for this user";
res.status(400).json(errors);
})

Categories

Resources