I am currently creating an animal shelter web app using mern and i have trouble grouping data by date month. so i have this schema for the rescued date:
const animalSchema = new mongoose.Schema({
date_rescued: {
type: Date,
default: Date.now,
},
})
module.exports = mongoose.model('Animal', animalSchema);
And here on my animal controller backend this is my query for fetching the datas:
exports.getRescuedChart = async(req,res,next) => {
const rescuedanimals = await Animal.find({}).select(['date_rescued']);
res.status(200).json({
success:true,
rescuedanimals,
})
}
the data that this function is returning to the state is this:
what i want to have is group them by data and count how many object has the same date.
rescuedanimals =[
{
date_rescued: "April",
animal_count: 8
}
]
so yeah i just learn how to do it myself so here's what i did.
first is that i installed dataeformat library: 'npm install dateformat'
i import it on my project: import dateFormat from 'dateformat';
what i did first is to change the date format and reduce duplicate dates.
const groups = rescuedanimals.reduce(
(groups, rescued_animal) => {
const date = dateFormat(rescued_animal.date_rescued[0], "mmmm")
if (!groups[date]) {
groups[date]=[]
}
groups[date].push(rescued_animal);
return groups;
}, {}
)
after that i created a new function to put this on an arraay and also to get the legnth of the objects that are in that date.
const groupArrays = Object.keys(groups).map((date) => {
return {
date,
games: groups[date].length
};
});
thanks to Austin Greco: https://stackoverflow.com/a/46802505/12398637
I'm creating data per day and I'm dealing with following response data ...
{
tipster: {
name: "Gallita FC",
description: "TEST",
picks: [{
date: "Friday, February 18th 2022",
data: [{
title: "yesterday",
description: "TEST",
date: "Friday, February 18th 2022",
category: "NHL",
pickImageUrl: "https://res.cloudinary.com/creaciones-inteligentes-roy/image/upload/v1644455039/Captura_de_Pantalla_2022-02-09_a_la_s_18.59.43_voy1pj.png",
}],
}, {
date: "Saturday, February 19th 2022",
data: [{
title: "today",
description: "TEST",
date: "Saturday, February 19th 2022",
category: "NHL",
pickImageUrl: "https://res.cloudinary.com/creaciones-inteligentes-roy/image/upload/v1644455039/Captura_de_Pantalla_2022-02-09_a_la_s_18.59.43_voy1pj.png",
}],
}],
imageUrl: "https://res.cloudinary.com/sports-master/image/upload/v1644649610/27ADF778-454B-4DB7-88B7-DC98202E2736_utb7xw.png",
bannerUrl: "https://scontent.fmex34-1.fna.fbcdn.net/v/t1.6435-9/167022015_1317341031983063_7337313589197318410_n.jpg?_nc_cat=111&ccb=1-5&_nc_sid=a26aad&_nc_ohc=5ctqP2nFf7IAX94PNSO&_nc_ht=scontent.fmex34-1.fna&oh=00_AT_TzRHhhV73ji7wzW2X1u27TOU8TNlObwtp0ILc0DzC1Q&oe=62207F2C",
id: "62075e5a13a43ace611fe5bd",
},
}
Within the tipster.picks array I need to append an additional data item to the last matching data item. A match could be where data.title equals "today".
The code I came up with so far does not lead to the correct result ...
const newPick = {
title,
description,
date,
category,
pickImageUrl,
};
const tipsterUpdate = {
...req.body,
picks: [...tipster.picks, tipster.picks.slice(-(1)[0], newPick)],
};
I'm using spread operator because I need to maintain the old data and only add a new object on the data array.
I really appreciate a little help here.
Thank you.
Destructure out the picks array from everything else in the tipster object, then build a new tipster object containing an updated picks array.
const data={tipster:{name:"Gallita FC",description:"TEST",picks:[{date:"Friday, February 18th 2022",data:[{title:"yesterday",description:"TEST",date:"Friday, February 18th 2022",category:"NHL",pickImageUrl:"https://res.cloudinary.com/creaciones-inteligentes-roy/image/upload/v1644455039/Captura_de_Pantalla_2022-02-09_a_la_s_18.59.43_voy1pj.png"}]},{date:"Saturday, February 19th 2022",data:[{title:"today",description:"TEST",date:"Saturday, February 19th 2022",category:"NHL",pickImageUrl:"https://res.cloudinary.com/creaciones-inteligentes-roy/image/upload/v1644455039/Captura_de_Pantalla_2022-02-09_a_la_s_18.59.43_voy1pj.png"}]}],imageUrl:"https://res.cloudinary.com/sports-master/image/upload/v1644649610/27ADF778-454B-4DB7-88B7-DC98202E2736_utb7xw.png",bannerUrl:"https://scontent.fmex34-1.fna.fbcdn.net/v/t1.6435-9/167022015_1317341031983063_7337313589197318410_n.jpg?_nc_cat=111&ccb=1-5&_nc_sid=a26aad&_nc_ohc=5ctqP2nFf7IAX94PNSO&_nc_ht=scontent.fmex34-1.fna&oh=00_AT_TzRHhhV73ji7wzW2X1u27TOU8TNlObwtp0ILc0DzC1Q&oe=62207F2C",id:"62075e5a13a43ace611fe5bd"}};
const newPick = {
title: 'Bob',
description: 'Bob does it again',
date: new Date(),
category: 'Bob',
pickImageUrl: 'bobImage',
};
// Accept data, the new pick, and a search
// (in this case "today")
function addNewPick(data, newPick, search) {
// Grab the picks, and then everything else
// from the tipster object
const { tipster: { picks, ...rest } } = data;
// `find` the index of the array containing the search text
const index = picks.findIndex(pick => {
return pick.data.some(obj => {
return obj.title === search;
});
});
// Add the new pick to the "today" array
picks[index].data.push(newPick);
// Return a new tipster object with
// the updated picks
return {
tipster: { ...rest, picks }
};
}
const out = addNewPick(data, newPick, 'today');
console.log(out);
quoting the OP
I'm using spread operator because I need to mantaint the old data and only add a new object on the data array.
Since spread syntax creates a shallow copy only, thus any nested level of the copy is still a reference and therefore in danger of being mutated, I suggest a one time deep clone via structuredClone (there are polyfills for environments which do not yet support this Web-Api method).
And as for a generic approach, which inserts a new data item after (either) the last data with a matching condition (or even after every condition matching data item), one needs a function which gets provided
the tipster.picks reference of the deeply cloned response data object,
the to be inserted new data item,
a callback function which implements the condition of a matching data item.
Within a first step one would collect a list of all data items where the condition does match. The second step is the insert task which can be adapted to maybe changing requirements ...
function insertDataItemAfterLastMatchingCondition(picks, item, condition) {
// collect a list of all data items where `condition` matches.
const matchList = picks
.reduce((matches, pickItem) => {
const { data } = pickItem;
const index = data.findIndex(condition);
if (index >= 0) {
matches.push({ array: data, index });
}
return matches;
}, []);
// insert new item excusivley after the last matching data item.
const { array, index } = matchList.at(-1) ?? {};
if (Array.isArray(array)) {
array.splice((index + 1), 0, item);
}
// // insert new item (copy) after every matching data item.
//
// matchList.forEach(({ array, index }) =>
// array.splice((index + 1), 0, {...item})
// );
}
const responseData = {tipster:{name:"Gallita FC",description:"TEST",picks:[{date:"Friday, February 18th 2022",data:[{title:"yesterday",description:"TEST",date:"Friday, February 18th 2022",category:"NHL",pickImageUrl:"https://res.cloudinary.com/creaciones-inteligentes-roy/image/upload/v1644455039/Captura_de_Pantalla_2022-02-09_a_la_s_18.59.43_voy1pj.png"}]},{date:"Saturday, February 19th 2022",data:[{title:"today",description:"TEST",date:"Saturday, February 19th 2022",category:"NHL",pickImageUrl:"https://res.cloudinary.com/creaciones-inteligentes-roy/image/upload/v1644455039/Captura_de_Pantalla_2022-02-09_a_la_s_18.59.43_voy1pj.png"}]}],imageUrl:"https://res.cloudinary.com/sports-master/image/upload/v1644649610/27ADF778-454B-4DB7-88B7-DC98202E2736_utb7xw.png",bannerUrl:"https://scontent.fmex34-1.fna.fbcdn.net/v/t1.6435-9/167022015_1317341031983063_7337313589197318410_n.jpg?_nc_cat=111&ccb=1-5&_nc_sid=a26aad&_nc_ohc=5ctqP2nFf7IAX94PNSO&_nc_ht=scontent.fmex34-1.fna&oh=00_AT_TzRHhhV73ji7wzW2X1u27TOU8TNlObwtp0ILc0DzC1Q&oe=62207F2C",id:"62075e5a13a43ace611fe5bd"}};
const responseClone = (typeof structuredClone === 'function')
&& structuredClone(responseData)
|| JSON.parse(JSON.stringify(responseData)); // fallback
const newData = {
title: 'tomoorow',
description: 'TEST',
date: 'Sunday, February 20th 2022',
category: 'NHL',
pickImageUrl: 'https://res.cloudinary.com/creaciones-inteligentes-roy/image/upload/v1644455039/Captura_de_Pantalla_2022-02-09_a_la_s_18.59.43_voy1pj.png',
};
insertDataItemAfterLastMatchingCondition(
responseClone.tipster.picks,
newData,
data => data.title === 'today',
);
console.log({ responseData, responseClone });
.as-console-wrapper { min-height: 100%!important; top: 0; }
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 }]
I have an array in my state and I want to set array specific values to its objects.
The code looks like this:
state = {
course: "",
type: "",
days: [
{
day: "",
rstime: "",
retime: "",
}
],
}
createSchedule = () => {
const { course,
module,
days
} = this.state;
data.push({
course,
module,
days[]
})
}
For now, I am able to set the state of course and module. I want to know how to set the days so that a course can have multiple days and different times.
The output should look like this for instance:
Course: Biology
Type: Lecture
days:
{
Monday
09:30
11:40
}
{
Friday
15:30
16:40
}
You can something like this,
const days = [...this.state.days];
days.push({
Friday
15:30
16:40
});
this.setState({days});
I have a list of elements with some attributes like
doc.title = 'some title'
doc.category = 'book'
doc.year = '2016'
other possible categories can be paper, article, etc.
Now, lets say there are two buttons 'Sort by Category' and 'Sort by Year'.
When 'Sort by Category' is clicked the list should like:
Book
1. book 1
2. book 2
Paper
1. paper 1
2. paper 2
Article
1. article 1
2. article 2
And when the 'Sort by Year' button is clicked
2016
1.
2.
3.
2015
1.
2.
and so on.
I want to do this while keeping the size of the html page as small as possible. Please let me know what is the best way to accomplish this.
Something like this should get you started - the code is commented with explanations (see it in action):
// convert array of objects into a Map grouped and ordered by supplied key
function group(items, key) {
// get unique values for grouping key
const unique = [
...new Set(items.map(item => item[key]))
];
// will be ascending by default
unique.sort();
// sorting all of the results by title field
const sortFn = (a, b) => a.title > b.title;
const sortItems = (val) => {
// filters the result set to items sharing the current group field value
let sorted = items.filter(item => item[key] === val);
// sort by title
sorted.sort(sortFn);
return sorted;
}
// reduce to a Map (which preserves insertion order and maintains the group key sorting)
return unique.reduce((map, cur) => map.set(cur, sortItems(cur)), new Map());
}
// testing it out
data = [{
title: 'foo',
category: 'book',
year: 2016,
}, {
title: 'bar',
category: 'book',
year: 2016,
}, {
title: 'blah',
category: 'paper',
year: 2010,
}, {
title: 'idk',
category: 'paper',
year: 2015,
}]
group(data, 'category'); // Map {"book" => [Object, Object], "paper" => [Object, Object]}
group(data, 'year'); // Map {2010 => [Object], 2015 => [Object], 2016 => [Object, Object]}
For displaying, you can use for...of with destructuring:
for (let [category, items] of group(data, 'category')) {
console.log(`
<div class="item">
<h3>${category}</h3>
<ol>${items.map(item => `<li>${item.title}</li>`).join('')}</ol>
</div>
`);
}
Note that even though I used ES6 throughout, this can easily be refactored to be more backwards compatible.