Return new Array with specific element [duplicate] - javascript

This question already has answers here:
From an array of objects, extract value of a property as array
(24 answers)
Closed 1 year ago.
I'm fetching data in React using axios like the following :
Hooks
const [date, setdate] = React.useState([]);
useEffect(() => {
axios
.get("http://localhost:8000/api/profilevisit/all/" + params.id)
.then((response) => {
setdate(response.data.data);
let NewArray = date.map((item, index) => {
return <span>{item.date}</span>;
});
console.log(NewArray);
})
.catch((err) => console.log(err));
}, []);
console.log(response.data.data):
(2) [{…}, {…}]
0: {date: "2021-05-15", views: 15}
1: {date: "2021-05-16", views: 6}
length: 2
__proto__: Array(0)
I would want to retrive from that array only date elements so I will have an array like this :
[2021-05-15, 2021-05-16]
I think I'm close but I can't put my finger on what's wrong as I created a loop in NewArray to get item.date but the NewArray in console doesn't returns anything. Any ideas ?
Edit : API direct response
{
"data": [
{
"date": "2021-05-15",
"views": 15
},
{
"date": "2021-05-16",
"views": 5
}
]
}

You can loop through the array and insert into another array. For example,
let dates = [];
response.data.data.forEach(item => {
dates.push(item.date);
});
console.log(dates); // array

This should work. You're setting a state and accessing it immediately which won't really work as its async. First set up a temp array. Then push the date item onto the temp array. Finally when all is said and done, set the state i.e setDate with that temp array. The dates would now be accesible in the desired format.
const [date, setdate] = React.useState([]);
useEffect(() => {
let tempArr = [];
axios
.get("http://localhost:8000/api/profilevisit/all/" + params.id)
.then((response) => {
response.data.data.forEach(item=>{
tempArr.push(item.date);
})
})
.catch((err) => console.log(err));
setDate(tempArr)
}, []);

if you want to get only elements that match a certain criteria use .filter()
myarray.filter(element => "add any logic here that returns true to keep element or false to remove it") in the end myarray will only have elements that match your criteria
for example with element needing to be older than 25
myarray = [{ name: 'max', age: 27 }, { name: 'paul', age: 25 }]
myarray.filter(element => element.age > 25)
myarray will be: [{ name: 'max', age: 27 }]

Related

JS - How to add key:value pairs from objects nested in arrays to other objects nested in another array

I know it has been countlessly asked and I assure you that I've read a lot of posts, articles, etc., and watched a lot of videos but nothing seems to click.
so there we go :
Here are 2 arrays with partial information about every person
let arr1 = [{id:00, name:Ben, city:Philadelphia}, {id:01, name:Alice, city:Frankfurt}, {id:02, name:Detlef, city:Vienna}]
let arr2 = [{id:02, age:18}, {id:00, age:39}, {id:01, age:75}]
And there is the desired final result: an array including the name, city, and age of each person
let arr3 = [{name:Ben, city:Philadelphia, age:39}, {name:Alice, city:Frankfurt, age:75 }, {name:Detlef, city:Vienna, age:18}]
What's the situation? Two arrays both containing objects. each nested object has an id. That id is the common key in each array of objects.
What do you want to do? : I want to create a third array including information from both arrays (from arr1: name and city; from arr2:age).
What have you tried so far? : I couldn't manage to achieve anything worth showing. this minimal example is intended to show you a simple example of my current situation which is: I've got an array that is in the LocalStorage on one hand and an API on the other, both contain some info regarding particular objects (let's say, persons). I want to create an array that will contain all the information regarding each person for easier manipulation afterward (DOM generation, etc.).
I've managed to store both arrays in two "local" arrays but the problem is still there: I can't figure out how to make an array where items are getting their key/value from two separate sources.
Thank you for your help!
You can use reduce method on the arr with array as an inital value, and inside try to find the corrospending item with same id and destruct the object from the id and merge the two object with spread operator.
let arr1 = [{id:00, name:'Ben', city: 'Philadelphia' }, {id:01, name:'Alice', city:'Frankfurt'}, {id:02, name:'Detlef', city:'Vienna'}]
let arr2 = [{id:02, age:18}, {id:00, age:39}, {id:01, age:75}]
const result = arr1.reduce((acc, { id: id1, ...rest1 }) => {
const { id: id2, ...rest2 } = arr2.find(i => i.id === id1)
acc.push({ ...rest1, ...rest2 })
return acc;
}, [])
console.log(result)
You can solve it in various ways, here first I have implemented a dict with key as id to get the value in O(1) while iterating arr2.
So the overall time complexity is O(n+k) where n is len of arr1 and k is len of arr2.
let arr1 = [{id:00, name: "Ben", city: "Philadelphia"}, {id:01, name:"Alice", city:"Frankfurt"}, {id:02, name:"Detlef", city:"Vienna"}];
let arr2 = [{id:02, age:18}, {id:00, age:39}, {id:01, age:75}];
const refMapById = arr1.reduce((refMap, {id, name, city}) => {
refMap[id] = {name, city};
return refMap;
}, {});
const result = arr2.reduce((resultArray, {id, age}) => [...resultArray, { ...refMapById[id],age}], []);
console.log(result);
Cheers!
It will be worth creating a dictionary from one of the arrays anyway since using .find() inside of .reduce() adds an unnecessary nested loop. But instead of reducing the second array as was suggested you can simply .map() it into the result array, like so:
let arr1 = [{ id: 00, name: "Ben", city: "Philadelphia" }, { id: 01, name: "Alice", city: "Frankfurt" }, { id: 02, name: "Detlef", city: "Vienna" }];
let arr2 = [{ id: 02, age: 18 }, { id: 00, age: 39 }, { id: 01, age: 75 }];
const groupedById = arr1.reduce((group, person) => {
group[person.id] = person;
return group;
}, {});
const result = arr2.map((personPartFromSecondArray) => {
const personPartFromFirstArray = groupedById[personPartFromSecondArray.id];
if (typeof personPartFromFirstArray !== "undefined") {
return { ...personPartFromFirstArray, ...personPartFromSecondArray }
}
return personPartFromSecondArray;
});
console.log(result);

Aggregating data after building multi-level array in javascript

I have a reduce function that is building multiple levels and is working perfectly for me except for one issue
Currently, it's building data based on employee first, then by date, area, and job. I'm getting all of the data at the proper level but I'm now trying to aggregate certain data for a totals section at the date level and it's just listing values rather than aggregating.
Basically, in the line I've notated below, I'd like to create a value called total_scans that simply adds up ALL scans for any orders on that date. In other words, for the record for Miranda on 8/12 I would expect the total_scans at the date level to have 49 as the value. Am I on the right track?
const nest = (rows) =>
rows.reduce(
(a, row) => {
const employee = a[row.employee] || (a[row.employee] = { dates: {} })
const date = employee.dates[row.job_date] || (employee.dates[row.job_date] = { areas: {} })
const order = date.areas[row.area_number] || (date.areas[row.area_number] = { jobs: {} })
const job = order.jobs[row.job] || (order.jobs[row.job] = { hours: '', scans: '', job_date: '' })
job.hours += row.hours
job.scans += row.scans
job.job_date = row.job_date
//this line is my issue
date.total_scans += job.scans
return a
},
{}
);
new Vue({
el: "#app",
props: {
},
data: {
rows: [
{
employee: "Miranda",
job: "123",
hours: "10",
job_date: "08/12/2021",
scans: 37,
area_number: "1234567",
},
{
employee: "Miranda",
job: "167",
hours: "15",
scans: 12,
job_date: "08/12/2021",
area_number: "1234568",
},
{
employee: "Miranda",
job: "184",
hours: "18",
scans: 24,
job_date: "08/13/2021",
area_number: "1234569",
}
],
},
computed: {
numbersByEmployee() {
return nest(this.rows)
},
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
{{numbersByEmployee}}
</div>
Your usage of reduce is a little irregular. The idea of reduce is to take an iterable (array) and return a single value, usually something like a String or Number.
Also, you're causing all sorts of side effects in your reducer, by modifying the object and arrays. Since Javascript is pass-by-reference for arrays and objects, those changes you're causing will be reflected in the original object, which is not how Vue prescribes things are done. If you want to modify data, it should be done in a watch, not a computed.
Finally, I believe you're overcomplicating your reduce function. Instead of a long reduce like that, you could simply do the below. Note the initialValue of 0.
const nest = (rows) =>
rows.reduce(
(sum, row) => {
return sum + row['scans'];
},
0
);
Obviously this will count all scans. If you want to only count scans by date, how about save yourself running the reducer across the array, and instead run filter first? Something like
const nest = (rows) =>
rows.filter(({job_date}) => job_date === SomeDate).reduce(...)
The {job_date} is a destructuring assignment. You could also split out a date filtered array into its own computed.

how to iterate over two responses one after another using javascript

I am initialized one variable with two responses from two async calls like this below.
const items = await service.fetchDropdownFieldItems(payload.url);
It is storing the responses one after another and it is working fine. Those responses are like below:
1st response : [{id:1, value:'Shoe'},{id:2,value:'Boutique'},{id:3,value:'Clothing'}]
2nd response: {data:[{country_id:1, country_name:'Australia'},{country_id:2,country_name:'France'},{country_id:3,country_name:'USA'}]}
Now, i want to format 2nd response's data array and replace the 2nd response with my formatted response.
Now, if i check items variable it should contain like this
1st response : [{id:1, value:'Shoe'},{id:2,value:'Boutique'},{id:3,value:'Clothing'}]
2nd response: [{id:1, value:'Australia'},{id:2, value:'France'},{id:3, value:'USA'}]}
Any approach for doing like this. There is no issue on async call or url etc. Only i want to change the response and replace with old one.
const formattedResponse = response.data.map((entry)=>({
id: entry.country_id,
value: entry.country_name
} ) )
you might place this logic somewhere inside service.fetchDropdownFieldItems, so you don't need to manually change your data everytime you fetch the items
Edit
Here is an example of how to use it inside a fetching function. You can change fetch with axios if you prefer
const formatResponse=(data)=>{
return data.map((entry)=>({
id: entry.country_id,
value: entry.country_name
} ) )
}
const fetchDropdownFieldItems =(url, options={})=>{
return fetch(url, options)
.then(res=> res.json())
.then(res=>res.data)
.then(formatResponse) //shorthand for .then(data=> formatResponse(data)
}
Use it as follows:
fetchDropdownFieldItems(...).then(doSomething)
//same as
fetchDropdownFieldItems(...).then(formattedData => doSomething(formattedData))
[].map should do the trick
const res2 = {
data: [{
country_id: 1,
country_name: 'Australia'
}, {
country_id: 2,
country_name: 'France'
}, {
country_id: 3,
country_name: 'USA'
}]
}
const result2 = res2.data.map(v => ({
id: v.country_id,
value: v.country_name
}))
console.log(result2)
const processData = (response) => {
const dataArray = [];
response.data.forEach(({country_id,country_name})=>{
dataArray.push({ id: country_id, value: country_name });
})
return dataArray;
}
This answer is the same as previous one but, takes less time to execute, and a little bit optimized
or also be used as
const processData = (response) => {
return response.data.map(({country_id,country_name})=>({ id: country_id, value: country_name }))
}

Match array value with value in an array of objects javascript

I am trying to work out how I can return a list with values from the own key in the array bellow if the object name value matches values in the lookupvalues array
lookupvalues = ["ross","linda"]
resources = [{own: "car", name: "bob"},{own: "bike", name: "ross"},{own: "plane", name: "linda"}]
wanted_output = ["bike","plane"]
I am struggling a bit with a good method to use for when I need to compare value in an object with array values. Is there a reasonable straight forward way to do this?
I must say how impressed I am that I got 4 replies with working examples at the same time!
One way (array method chaining) is that you could filter by name and map to grap each's own
const lookupvalues = ["ross", "linda"]
const resources = [
{ own: "car", name: "bob" },
{ own: "bike", name: "ross" },
{ own: "plane", name: "linda" },
]
const res = resources
.filter(({ name }) => lookupvalues.includes(name))
.map(({ own }) => own)
console.log(res)
resources.filter(resource => lookupvalues.includes(resource.name))
.map(resource => resource.own);
This will filter by the items that have names that are included in lookupvalues, and then transform the array into an array of the own values of those remaining.
You can take the help of Array#filter and Array#map:
const lookupvalues = ["ross","linda"]
const resources = [{own: "car", name: "bob"},{own: "bike", name: "ross"},{own: "plane", name: "linda"}]
const filterRes = (arr) => {
const lookup = new Set(lookupvalues);
return arr.filter(({name}) => lookup.has(name))
.map(({own}) => own);
}
console.log(filterRes(resources));
resources.filter(item => lookupvalues.indexOf(item.name) > -1).map(item => item.own)

Filter array of objects based on the input passed: Javascript

I have an array of objects with the following structure
arr = [ { name: "abc" , items: ["itemA","itemB","itemC"], days :138} ,
{ name: "def" , items: ["itemA1","itemB2","itemC1"], days :157} ,
{ name: "hfg" , items: ["itemAN","itemB7","itemC7"], days :189} ]
This array needs to be filtered based on the search input passed. I was able to achieve the same for the name , where days is not getting filtered.
Also can someone help how to search across items array too so it filters the rows based on input passed
This is what I have tried
handleSearch = (arr, searchInput) => {
let filteredData= arr.filter(value => {
return (
value.name.toLowerCase().includes(searchInput.toLowerCase()) ||
value.days.toString().includes(searchInput.toString())
);
});
console.log(filteredData);
//this.setState({ list: filteredData });
}
You can use Array#some and then perform the same kind of match that you've already done :
The some() method tests whether at least one element in the array passes the test implemented by the provided function. It returns a Boolean value.
handleSearch = (arr, searchInput) => {
const filteredData = arr.filter(value => {
const searchStr = searchInput.toLowerCase();
const nameMatches = value.name.toLowerCase().includes(searchStr);
const daysMatches = value.days.toString().includes(searchStr);
const oneItemMatches = value.items.some(item => item.toLowerCase().includes(searchStr));
return nameMatches || daysMatches || oneItemMatches;
});
console.log(filteredData);
//this.setState({ list: filteredData });
}
As your search value can apply to all fields in your data array, you can combine the values together in one array (row by row) and perform the search in one place.
To do that, I've provided a snippet below that will filter the original array checking each object's values after the transformations. These involve using Object.values() to get the values of the object in an array, since this array is nested, we can make use of Array.flat() to flatten it into just the strings and numbers, finally call Array.some() to check if one of the values partially includes the search value (after they've both been lowercase-d).
const arr = [
{ name: "abc" , items: ["itemA","itemB","itemC"], days: 138 },
{ name: "def" , items: ["itemA1","itemB2","itemC1"], days: 157 },
{ name: "hfg" , items: ["itemAN","itemB7","itemC7"], days: 189 }
];
const handleSearch = (arr, searchInput) => (
arr.filter((obj) => (
Object.values(obj)
.flat()
.some((v) => (
`${v}`.toLowerCase().includes(`${searchInput}`.toLowerCase())
))
))
);
console.log('"A1" =>', JSON.stringify(handleSearch(arr, 'A1')));
console.log('189 =>', JSON.stringify(handleSearch(arr, 189)));
console.log('"nope" =>', JSON.stringify(handleSearch(arr, 'nope')));
NOTE: This approach has one obvious flaw, it will seach through numbers as strings, meaning that providing 89 as the search value will still return the second element.

Categories

Resources