Fetch pokemon API forEach issue - javascript

I need help with the forEach method. I am trying to mess around with the Pokemon API, but I can't run a forEach method without it returning undefined.
When I tried to use forEach instead of the map (I noticed map returned one big array) it still returned undefined.
fetch('https://pokeapi.co/api/v2/pokemon?limit=151')
.then(res => res.json())
.then(data => fetchPokemon(data))
const fetchPokemon = (res) => {
console.log(res)
// This turns the object api into an array
const arr = [res];
console.log(arr)
// This creates a variable to map correctly
const firstArr = arr[0].results
// This creates a new array with pokemon name
const array = firstArr.map(pokemon => pokemon.name)
console.log(array);
const html =
`
<div>${array}</div>
`
const pokemon = document.querySelector('.pokemon');
pokemon.innerHTML = html;
}

So there's a few things going on:
You don't need ".then(res => res.json())" because the response is already in json. You'll also want to extract the data property specifically like:
axios('https://pokeapi.co/api/v2/pokemon?limit=151')
.then(({data}) => fetchPokemon(data))
"const arr = [res];" does not turn the object into an array but rather places it into an array. Also, there's a further result layer you need to extra, so you'd want to instead do:
const fetchPokemon = (res) => {
//extract the results property into its own variable
const {results} = res;
//create an array of just object.name properties
const names = results.map( pokemon => pokemon.name)
}
Finally you can use the map property to create an array of just the pokemon names.

The forEach() method calls a function once for each element in an array, in order.
forEach: This iterates over a list and applies some operation with
side effects to each list member (example: saving every list item to
the database)
map: This iterates over a list, transforms each member of that list,
and returns another list of the same size with the transformed members
(example: transforming list of strings to uppercase)
from here
Meaning, if you want to do something for every element of the array, foreach is the correct function.
Example of Array.ForEach
var a = ["a", "b", "c"];
a.forEach(function(entry) {
console.log(entry);
});
Now, you are trying to create a new array with all the pokemon names, therefore map is correct - you are mapping the array into a new array.
However, if you want to, say, make an li element in html for every name, then it would be correct to use ForEach.
Example:
fetch('https://pokeapi.co/api/v2/pokemon?limit=151')
.then(res => res.json())
.then(data => fetchPokemon(data))
const fetchPokemon = (res) => {
const firstArr = [res][0].results
var ul = document.getElementById("pokemon");
firstArr.forEach(function (entry) {
var li = document.createElement('li');
li.appendChild(document.createTextNode(entry.name));
ul.appendChild(li);
});
}
<ul id="pokemon"></ul>

The forEach method is supposed to return undefined.

Related

Javascript reduce taking too much time to convert array into object

I have 2 lists of array. One having 50 elements(users) and other with 20000 elements(allUsers). I have to filter users based on if they are present in allUsers.
For that, i am converting allUsers into object and using that object to filter out users.
allUsers is an array of object with 3 keys.
So, creating object from array is taking too much of time. How can i reduce the time of the overall operationn?
const usersMap = allUsers.reduce((aa, user) => {
acc = { ...aa, [user.id]: user }
return aa
}, {})
const matchedUsers = users.reduce((aa, user) => {
const mappedUser = usersMap[user.id]
if (mappedUser) {
aa.push(mappedUser)
}
return aa
}, [])
Your use of spread is definitlely going to slow this down. { ...aa, [user.id]: user } creates a new object every iteration rather than just adding a new property to it, and in so doing has to iterate every property of the spread object again making your reduce approach O(n^2) rather than being O(n).
You can start by making your reduce more efficient by removing the unnecessary spread and simply assigning a new property in the accumulator.
const usersMap = allUsers.reduce((aa, user) => {
aa[user.id] = user;
return aa;
}, {});
Or you can try using a Map
const usersMap = new Map(allUsers.map((user) => [user.id, user]));
const matchedUsers = users.reduce((aa, user) => {
if (usersMap.has(user.id)) {
aa.push(usersMap.get(user.id));
}
return aa;
}, []);
const lookup = new Set(users.map(x => x.id));
const matchedUsers = allUsers.filter(x => lookup.has(x.id));
Instead of
Building a map of 20k items
Go through 50 items trying to relate them back to the map
Return an array of all the map values that match
Flip the operation around and
Get a set of 50 IDs to match.
Extract the up to 50 items from allUsers that have a matching ID.
Since there is no object created from the 20k items, this reduces the time to create one to zero.
users.filter(user => allUsers.find(u => u.id === user.id))
Filter the users that aren't found in all the users.

JavaScript Objects filtering out specific property names

I am creating a filter for a diagram. Whenever a filter is clicked it should remove that category from the diagram. The api call returns an object with names. let’s say the object returns 40 items and I want to filter 5 out by name. What is the best way to approach this?.
I tried to manually type the property names into an array and run the .filter on my object like below. However it returns the entire object unfiltered.
filterDiagram() {
Const array = [“all the names of the properties I want to filter out”]
carbonates = array.forEach(x) => {console.log(x)}
Const filterCat = data.filter (io =>
io.name !== carbonates)
}
Let's say, the array consists of all the names/categories you want to take out.
const toBetakenOut = ['fruits','salts', 'etc' ]
// Please make sure they are not 1 string but rather comma-separated values.
You can filter the API data by using the filter function on the data,to remove objects with names that are within toBetakenOut.
const filterCat = data.filter (io => !toBetakenOut.includes(io.name))
function filterDiagram(){
const dontWantsArray = [/*all of the things you dont want*/];
// Outputs an array of keys
const filteredKeys = Object.keys(yourJSObject)
.filter(key => !dontWantsArray.includes(key));
// After you found the keys you can get the values to another array by keys
const filteredValues = filteredKeys.map(key => yourJSObject[key]);
}

How to add and update objects into array of objects?

I am trying to dynamically add an object to an array of objects, I have been trying to Destructuring the main object but it adds a number to the end of the parent array. This is what I have:
const [data, setData] = useState ([
{
_id:1,
firstName:'Leo',
lastName:'Miller',
telephone:'+569273829',
mail:'leo.miller#gmail.com',
work:[
{_id:1, startWorkDate:'01/01/2015', endWorkDate:'01/02/2017', work:'description...'},
{_id:2, startWorkDate:'01/01/2018', endWorkDate:'01/02/2020', work:'description...'}
]
}];
I generate dynamically this object:
const value = {_id:3, startWorkDate:'01/01/2018', endWorkDate:'01/02/2020', work:'description...'}
I need to add it into data.work and after that update only the description of work._id[3]
I try with this function
const addNewWork = (value) => {
let copyData = [...data, data[0].workExperience.push(value)]
return setData(copyData)
}
but for some reason doesn't add correctly the object. Help please!
You have an array and not an object. Your statement
let copyData = [...data, data[0].workExperience.push(value)]
is doing two things:
mutating the state by doing push(). Which is not the react way.
creating a new array. Also adding a new item to the array, but that is the new length of data[0].workExperience.
The return value of Array.prototoype.push is:
The new length property of the object upon which the method was called.
What you have to do is:
Make a copy of the array. Can use ... (spread operator) here.
Make a copy of the array object you want (first index). Try to add the object to its specific property workExperience.
const addNewWork = (value) => {
let newData = [...data];
let newWorkExperienceArray =
[...data[0].workExperience,value];
let newDataFirstObject = {...data[0], workExperience : newWorkExperienceArray};
newData[0] = newDataFirstObject;
return setData(newData)
}
You can also update the property. I didn't find the relevant code in your question as to what I have to update so I didn't update anything in the third workExperience object.
EDIT: It seems in your code the property name is work and not workExperience. Please confirm. The above code uses workExperience, you can replace it by work if that is the case
You can do this with this simple function:
const addNewWork = (value) => {
let updatedObj = data[0];
updatedObj.work.push(value)
// updates your object each time
let copyData = [updatedObj]
// adds a new object for each call
// let copyData = [...data, updatedObj]
return setData(copyData)
}
Now it updates the object in your state. If you want to add a new object for each call just uncomment let copyData = [...data, updatedObj] and comment out let copyData = [updatedObj]
When you set state for array your setter is a quite bite different
setData(prevData => [...prevData, newItem]) // to add a single item to array
setData(prevData => newArray) // to replace entire array

how to push the data from a select in a single array?

I'm using react-select, my api expects that i send an array of strings like this ["purple", "red", "orange"] but react-select gives me an array of objects, so what I did was map throught newValue picking each value and putting in an vaiable array, but i'm getting each value in separated arrays like this ["purple"], ["red"], ["orange"]
handleChange = (newValue: any, actionMeta: any) => {
console.log("newValue-->",newValue);
newValue.map((obj: any) => {
const array = [];
array.push(obj.value);
console.log("array-->",array);
});
complete code:
https://codesandbox.io/s/react-codesandboxer-example-sf7tz?file=/example.js
Initialize your array outside before map.
Otherwise you are creating new array for each object.
const array = [];
newValue.map((obj: any) => {
array.push(obj.value);
console.log("array-->",array);
});
As per the react select docs, it should be an array of objects with value and label. so in your case you can do in the below way.
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
let input = ["purple", "red", "orange"]
let output = input.map(item => ({value: item, label: capitalizeFirstLetter(item) }))
console.log(output)
Explanation on the Question snippet
newValue.map((obj: any) => {
const array = [];
array.push(obj.value);
console.log("array-->",array);
});
Here you are mapping through each object, then every time mapping you are creating an empty array, to that array you push the value and print it.
So take first item as example, red iterates -> empty array created -> push the red value to empty array and then printing it.
So this loops three times since the length of the array is 3, so three times you get the array.
Fix on the question snippet is declare the array outside the loop and push in the same manner as the doc suggest then you can pass to react select component as options props and it will work as expected.

Remove Strings From Array

I'm trying to make an array of objects from the browsing history that can become links in a React list.
What this does is takes the history element and pushes it to the user object, then I take the name and ID, and splice it back into the array. I then filter it down to distinct values.
What I am left with, is an array that looks like this:
[{id1,name1},{id2,name2},{id3,name3},"id1","id2","id3"]
which is almost exactly what I want, except if I'm going to map that to create links, I need to get rid of the string values.
It occurred to me to try skipping the history element and just make an array of matches, and any number of other things.
pop and shift don't work to isolate the first object because then the whole function continually returns a single item.
This is the only way I have gotten a result close to what I want, I just need a simple way of filtering out string values from an array after it's created, or maybe before it's mapped out.
const getLead = async _id => {
const res = await axios.get(`/api/leads/${_id}`);
dispatch({
type: GET_LEAD,
payload: res.data
});
const { name } = res.data
const match = { name, _id }
const setPrevious = () => {
const prevLeads = user.prevLeads
const history = createBrowserHistory(getLead);
const prevLead = history.location.pathname.slice(6);
prevLeads.push(prevLead);
return prevLeads;
}
const prevLeads = setPrevious();
const [...spliceLeads] = prevLeads.splice(0,1,match,match);
const distinct = (value, index, self) => {
return self.indexOf(value) === index;
}
const recentLeads = prevLeads.filter(distinct) ;
console.log(spliceLeads)
}
You just check the type in your .filter logic:
const array = [
{ test: 'ing' },
1,
new Date(),
'this should be removed'
];
const result = array.filter(e => typeof e !== 'string');
console.log(result);
The solution what has been provided already works like charm, that should be used for your solution, below I'm providing a bit different way to figure out if the element is a string or not.
I guess filter() is a good way to test each elements on an array and return only the passing once as a result. If you call on each item Object.prototype.toString.call(e) then it will return as a text if you have [object String] or something else. This can be also used to filter out your string values from your array.
See the example below:
const prevLeads = [{id:'id1',name: 'name1'},{id: 'id2',name:'name2'},{id:'id3',name:'name3'},"id1","id2","id3"];
const filter = e => Object.prototype.toString.call(e) !== '[object String]';
const recentLeads = prevLeads.filter(filter);
console.log(recentLeads);
I hope that helps!

Categories

Resources