I am using React and redux to build an 'appointment schedule' feature for my app. At this point, I am receiving my array of objects where each object represents an appointment with properties 'date' and 'startTime'. My question is how can I sort my array by both date and time so that sooner appointments come first?
Here is my reducer (upcomingAppointments is the variable where I am storing the array):
case 'SCHEDULE_APT_SUCCESS':
//console.log('actions payload: ', action.payload )
return {
...state,
isSchedulingApt: false,
newlyAddedApt: action.payload,
aptScheduleDone: true,
upcomingAppointments: [...state.upcomingAppointments, action.payload]
};
Thanks in advance.
Let suppose your array looks something like this:
const upcomingAppointments = [
{ date: 'Wed Jun 03 2020', time: '11:39:04' },
{ date: 'Tue Jun 02 2020', time: '16:03:04' },
{ date: 'Wed Jun 03 2020', time: '17:59:12' },
// ...
];
Using Array.prototype.sort(), you can provide a custom sort function as argument:
const sortedAppointments = upcomingAppointments.sort((a, b) => {
const dateA = Date.parse(a.date + ' ' + a.time);
const dateB = Date.parse(b.date + ' ' + b.time);
// Sort in descending order
return dateA < dateB ? 1 : -1;
});
Related
I have an array of objects "mainData" like so:
0: {date: "2020-07-25T16:44:43.000Z"
description: "Qwerty"
id: 89329972},
1: {date: "2020-07-25T16:46:28.000Z"
description: "Place bins please"
id: 65586316},
2: {date: "2020-07-25T16:49:12.000Z"
description: "Solve sewerege problem"
id: 84687816},
3: {date: "2020-07-27T16:34:47.000Z"
description: "Test compl"
id: 56437370},
4: {date: "2020-07-28T08:40:34.000Z"
description: "Sewerage problem in my area"
id: 92402221},
5: {date: "2020-09-09T11:42:18.000Z"
description: "problem"
id: 25613902},
Now I am allowing the user to select from and to dates by using the mui datepicker. This is how I am receiving the values:
fromDate: Sat Jul 25 2020 11:43:00
toDate: Sat Aug 08 2020 11:43:00
Now I want to filter the array from this date to that date, including the from and to dates. I tried to do it this way but it just returns an empty array. I've put the code inside useEffect which is run every time toDate changes, Also I've used Moment to make the formats of both dates same:
useEffect( () => {
if (fromDate !== null && toDate !== null) {
setReportData(
mainData.filter(
(obj) =>{
return Moment(obj.date).format("DD MMM yyyy") >= Moment(fromDate).format("DD MMM yyyy") && Moment(obj.date).format("DD MMM yyyy") <= Moment(toDate).format("DD MMM yyyy")
}
)
)
}
},[toDate])
Edit
When I select a single date :
useEffect( () => {
if (oneDate !== null) {
setReportData(
mainData.filter(
(obj) =>{
return new Date(obj.date.substring(0, 19)).getTime() === oneDate.getTime()
}
)
)
}
},[oneDate])
Your object's date property can be parsed directly to Date object. So then you can use getTime.
Also, filter returns Date object.
So, you can change your code to this
useEffect( () => {
if (fromDate !== null && toDate !== null) {
setReportData(
mainData.filter(
(obj) =>{
return new Date(obj.date).getTime() >= fromDate.getTime() && new Date(obj.date).getTime() <= toDate.getTime()
}
)
)
}
},[toDate])
If you want to consider all dates to be of local timezone, then you need to remove the last part of each date's string in order for the parse method to consider each string as local timezone date.
So previous method becomes
useEffect( () => {
if (fromDate !== null && toDate !== null) {
setReportData(
mainData.filter(
(obj) =>{
return new Date(obj.date.substring(0, 19)).getTime() >= fromDate.getTime() && new Date(obj.date.substring(0, 19)).getTime() <= toDate.getTime()
}
)
)
}
},[toDate])
We can use moment.js too. By converting the moment to expected format.
In above case we have
Sat Jul 25 2020 11:43:00
Moment provides locale support format using llll, which is similar
to this one, usage as follow.
Initialize the format constant somewhere at top, after if statement;
const format = 'llll';
And just replace the filter return statement with :
return Moment(obj.date, format).unix() >= Moment(fromDate, format).unix() && Moment(obj.date, format).unix() <= Moment(toDate, format).unix()
I have a data structure like this:
const _ = require('lodash');
const bills = [
{year:2021, month:5, bill:'bill in 2021 may'},
{year:2018, month:1, bill:'bill in 2018 jan'},
{year:2019, month:1, bill:'bill in 2019 jan'},
{year:2018, month:2, bill:'bill in 2018 feb'},
{year:2019, month:10,bill:'bill in 2019 oct'},
{year:2019, month:2, bill:'bill in 2019 feb'},
{year:2019, month:6, bill:'bill in 2019 jun'},
{year:2020, month:11,bill:'bill in 2020 nov'}
];
and I want to display like below using Text or Card component of native-base
2018
1
bill in 2018 jan
2
bill in 2018 feb
2019
1
bill in 2019 jan
2
bill in 2019 feb
6
bill in 2019 jun
10
bill in 2019 oct
2020
11
bill in 2020 nov
2021
5
bill in 2021 may
My codes are below using lodash library to generate above and display in the terminal
// sort the data first
let arrSortedTasks = _.orderBy(tasks, ['year', 'month'],['asc']);
// get all the different year from the data
let arrUniqYear = _.uniqBy(arrSortedTasks, 'year');
// get all the different month from the data
let arrUniqMonth = _.uniqBy(arrSortedTasks, 'month');
// take out only the value of the year
arrUniqYear =_.map(arrUniqYear, 'year');
// take out only the value of the month
arrUniqMonth =_.map(arrUniqMonth, 'month');
let taskList = '';
for (let year of arrUniqYear) {
console.log(year);
for (let month of arrUniqMonth) {
let displayMonth = false;
for (let obj of arrSortedTasks) {
if (obj.year === year && obj.month === month) {
taskList = taskList + obj.task;
displayMonth = true;
}
}
if (displayMonth) {
console.log(" " + month);
}
if (taskList.length > 0) {
console.log(" " + taskList);
}
taskList = '';
}
}
How can we display the components in react-native with native-base? SO here don't let me post if too many code sigh. I tried a few ways buy got errors and can't figure out.
I end up using array as a return object in rendering
renderBillsSection() {
const { bills } = this.props;
if(bills || bills.length > 0 ) {
let arrSortedTasks = _.orderBy(tasks, ['year', 'month'],['asc']);
let arrUniqYear = _.uniqBy(arrSortedTasks, 'year');
let arrUniqMonth = _.uniqBy(arrSortedTasks, 'month');
let billList = '', arr = [], yearIndex = 0, monthIndex = 0, billIndex = 0;
arrUniqYear = _.map(arrUniqYear, 'year');
arrUniqMonth = _.map(arrUniqMonth, 'month');
for (let year of arrUniqYear) {
arr.push(<Text key="{yearIndex}">{year}</Text>)
yearIndex++
for (let month of arrUniqMonth) {
let displayMonth = false;
for (let obj of arrSortedTasks) {
if (obj.year === year && obj.month === month) {
billList = billList + obj.task
displayMonth = true
}
}
if (displayMonth) {
arr.push(<Text key="{monthIndex}" style={{marginLeft:10}}>{month}</Text>)
monthIndex++
}
if (billList.length > 0) {
arr.push(<Text key="{taskIndex}" style={{marginLeft:20}}>{billList}</Text>)
billIndex++
}
billList = '';
}
}
return arr;
}
}
not sure about how you are planning to render it in UI, but if you want to have the data structure like this, you need to group it (and sort by monhts if its not already sorted)
_(bills).groupBy('year').map((v,k)=> ({year: k, docs: _.sortBy(v,'month')})).value()
it will give you another array where you have year, abd docs as nested array holding all the documents of that year, so that you can agaib have another repeat on that.
const bills = [
{year:2021, month:5, bill:'bill in 2021 may'},
{year:2018, month:1, bill:'bill in 2018 jan'},
{year:2019, month:1, bill:'bill in 2019 jan'},
{year:2018, month:2, bill:'bill in 2018 feb'},
{year:2019, month:10,bill:'bill in 2019 oct'},
{year:2019, month:2, bill:'bill in 2019 feb'},
{year:2019, month:6, bill:'bill in 2019 jun'},
{year:2020, month:11,bill:'bill in 2020 nov'}
]
let groupedDoc = _(bills).groupBy('year').map((v,year)=> ({year, docs: _.sortBy(v,'month')})).value();
console.log(groupedDoc);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"></script>
Here is a working snippet:
I think you should do :
const gropuByYear = _.groupBy(bills,'year');
console.log(_.map(groupByYear, groups =>
groups.forEach(group=>(<View> {obj.bill}</View>)))
even you can orderBy year desc first then do the loop good luck
You need to take a look at SectionList of React Native.
Checkout this example and cuiyueshuai for more practical example.
SectionList Demo:
<SectionList
renderItem={({ item, index, section }) => <Text key={index}>{item}</Text>}
renderSectionHeader={({ section: { title } }) => <Text style={{ fontWeight: 'bold' }}>{title}</Text>}
sections={[
{ title: 'Title1', data: ['item1', 'item2'] },
{ title: 'Title2', data: ['item3', 'item4'] },
{ title: 'Title3', data: ['item5', 'item6'] },
]}
keyExtractor={(item, index) => item + index} />
I'm usually used to play with the Object.keys() function.
But this time, I'm having trouble to read the value of the properties by their name, for each object within the following JSON :
The JSON
var myData = {
"customNotes":{
"2017/04/17":{
"concernedDate":"April, 17, 2017",
"notesList":[
{
"id":25,
"title":"Note 25 Title"
},
{
"id":51,
"title":"Note 51 Title"
}
]
},
"2017/04/14":{
"concernedDate":"April, 14, 2017",
"notesList":[
{
"id":53,
"title":"Note 53 Title"
}
]
}
}
}
The OUTPUT I Need
What I Need is the following OUTPUT :
2017/04/17
concernedDate: April, 17, 2017
Number of notes: 2
Notes list:
- id: 25
- title: Note 25 Title
- id: 51
- title: Note 51 Title
- - - - - - - -
2017/04/14
concernedDate: April, 14, 2017
Number of notes: 1
Notes list:
- id: 53
- title: Note 53 Title
My buggy JS Code
$(Object.keys(myData.customNotes)).each(function(iGroup){
//Get Key/Val of each Group of notes
var keyGroup = Object.keys(myData.customNotes)[iGroup];
var valGroup = myData.customNotes[keyGroup];
//Print Key (the date as a string)
console.log(valGroup[0]);
//Print Key Property ('concernedDate')
console.log('concernedDate: ' + valGroup[0].concernedDate);
//Print Key Property Length ('notesList')
console.log('Number of notes: ' + valGroup[0].notesList.length);
//Print List of notes
console.log('Notes list:');
//If the property exists
if(valGroup[0].notesList){
//For each item in 'notesList'
$(Object.keys(valGroup[0].notesList)).each(function(iNote){
//Get Key/Val For each note
var keyNote = Object.keys(valGroup[0].notesList)[iNote];
var valNote = valGroup[0].notesList[keyNote];
//Print the properties of each note
console.log('- id: ' + valNote.id);
console.log('- title: ' + valNote.title);
});
}
});
es6
Object.keys(myData.customNotes).reduce((prev, curr) => {
const date = curr;
const concernedDate = myData.customNotes[curr].concernedDate;
const numberNotes = myData.customNotes[curr].notesList.length;
const notesList = myData.customNotes[curr].notesList.map(note => `- id: ${note.id} \n - title: ${note.title} \n\n`);
return prev + `${date} \n ${concernedDate} \n Number of notes: ${numberNotes} \n Notes list: \n ${notesList} \n - - - - - - - - \n`;
}, ''));
es5
Object.keys(myData.customNotes).reduce(function (prev, curr) {
const date = curr;
const concernedDate = myData.customNotes[curr].concernedDate;
const numberNotes = myData.customNotes[curr].notesList.length;
const notesList = myData.customNotes[curr].notesList.map(function(note) { return ‘- id: ‘ + note.id + ‘\n’ + ‘- title: ‘ + note.title + ‘\n\n’;});
return prev + 'date' + '\n' + concernedDate + '\n Number of notes: ' + numberNotes + '\n Notes list: \n' + notesList + ' \n - - - - - - - - \n';
}, ''));
First, I want to thank #cheesenthusiast for its answer: It works like a charm, and you should definitely rely on it, wether it is in ES6 or ES5 !
In the same time, I've experimented by myself, and found another working solution, in a more "classic loopy-style" way of achieving the same result.
Another working method (more classical)
So for those of you that want to have more control at every step of the script, Here is another working way to do it :
//WORKING
console.log('---START LIST---');
//For each GROUP OF NOTES
for (var item in myData.customNotes) {
//Print GROUP OF NOTES key
console.log(item);
//Print GROUP OF NOTES properties
console.log('concernedDate: ' + myData.customNotes[item].concernedDate);
console.log('Number of notes: ' + myData.customNotes[item].notesList.length);
console.log('Notes list: ');
//For each NOTE
$(Object.keys(myData.customNotes[item].notesList)).each(function(iGroup){
//Get this Array item
var keyGroup = Object.keys(myData.customNotes[item].notesList)[iGroup];
var valGroup = myData.customNotes[item].notesList[keyGroup];
//Print NOTE properties
console.log('- id: ' + valGroup.id);
console.log('- title: ' + valGroup.title);
});
console.log('- - - - - -');
}
console.log('---END LIST---');
SORTING
After having added some elements to my Object, I need to sort it by KEY (the date : example 2017/04/17).
But it always returns the same items in the same order, regardless of what I try :
//Creating an Array of SORTED values (Nothing works :-/)
var tmpArr = Object.keys(myData.customNotes).sort((a, b) => a > b ? a : b);
var tmpArr = Object.keys(myData.customNotes).sort((a, b) => Date(a).valueOf() > Date(b).valueOf() ? a : b);
var tmpArr = Object.keys(myData.customNotes).sort((a, b) => a.valueOf() > b.valueOf() ? a : b);
var tmpArr = Object.keys(myData.customNotes).sort((a, b) => a-b);
//Convert back the Array to an Object
var myData.customNotes = tmpArr.reduce(function(acc, cur, i) {
acc[i] = cur;
return acc;
}, {});
Hello all i am designing a leave management website with angularjs and ui-calendar.If a user takes a leave ,the values are taken from the database and displayed as an event in the calendar.Now what i want to do is ,if the user is not absent on particular day,it should be displayed as present event.Hope the following image helps understanding better.
Now vikki is taking leave on friday.I want to mark other dates as an event displaying in different color saying he s present.I need this to be in the week view.Please let me know if there is any way to do this thing.Following is my code
app.factory('calendarSer', ['$http','$rootScope', 'uiCalendarConfig', function ($http,$rootScope, uiCalendarConfig) {
return {
displayCalendar: function($scope) {
$calendar = $('[ui-calendar]');
var date = new Date(),
d = date.getDate(),
m = date.getMonth(),
y = date.getFullYear();
$scope.changeView = function(view) {
$calendar.fullCalendar('changeView', view);
};
/* config object */
$scope.uiConfig = {
calendar: {
lang: 'da',
height: 450,
editable: true,
selectable: true,
header: {
left: 'month basicWeek basicDay',
center: 'title',
right: 'today prev,next'
},
eventClick: function(date, jsEvent, view) {
$scope.alertMessage = (date.title + ' was clicked ');
alert("clicked" + date.title);
},
select: function(start, end, allDay) {
var obj = {};
obj.startAt = start.toDate();
obj.startAt = new Date(obj.startAt).toUTCString();
obj.startAt = obj.startAt.split(' ').slice(0, 4).join(' ');
obj.endAt = end.toDate();
obj.endAt = new Date(obj.endAt).toUTCString();
obj.endAt = obj.endAt.split(' ').slice(0, 4).join(' ');
$rootScope.selectionDate = obj;
$("#modal1").openModal();
calendar.fullCalendar('unselect');
},
eventRender: $scope.eventRender
}
};
$scope.events = [];
$scope.eventSources = [$scope.events];
$http.get("rest/leave/list", {
cache: true,
params: {}
}).then(function(data) {
$scope.events.slice(0, $scope.events.length);
angular.forEach(data.data, function(value) {
console.log(value.title);
$scope.events.push({
title: value.title,
description: value.description,
start: value.startAt,
end: value.endAt,
allDay: value.isFull,
stick: true
});
});
});
}
}
}]);
Thanking you
You need to also create the events array which would display the user is present. However, if you try to create the array in the front-end, then you would not know the other user information to fill the calendar.
"rest/leave/list" : will return that vikki is on leave, however what if the other user that has not taken any leave and is not returned in this array? how will you be able to fill the calendar saying user is present all the other days?
$scope.events.push({
title: value.title,
description: value.description,
start: value.startAt,
end: value.endAt,
allDay: value.isFull,
stick: true
});
$scope.eventSources = [$scope.events];
You are filling the events and binding it to the eventSources.
So you need to return something like below from the reponse "rest/leave/list":
{
title: "vikki",
description: "description",
startAt: "2017-05-05 00:00",
endAt: "2017-05-05 23:59",
isFull: true,
leave: true <- This will say he is absent
},
{
title: "vikki",
description: "description",
//The start and end date time will control the block that will be booked in the calendar
startAt: "2017-06-05 00:00",
endAt: "2017-01-06 23:59",
isFull: true,
leave: false <- This will say he is present
//This array will book the calendar from May-06 to end of the month.
//If you want the past, then create one in the past and send it from
//server
}
In the above array, you need to create separate rows for absent and present. For example , 1st row consist of January month where the user has not taken any leaves, so you create a row with Start date Jan 01 and End date Jan 30, In Feb, the user has taken one leave on say 5th. So you create three rows, row 1 with Feb 01 to Feb 04 as present, row 2 with Feb 05 as absent, and row 3 with Feb 06 - Feb 31 as present
Using the variable "leave" from the array, in the frontend you can change the colour. You can refer it from this how to achieve it.
Jquery Full calendar and dynamic event colors
I have a JSON REST service that returns a list of periods with start dates. How can I convert the start date to javascript date?
[ {
"periodId": 1234,
"startDate": [ 2011, 12, 24]
},
{
"periodId": 5678,
"startDate": [ 2012, 12, 24]
}
]
I'm using ngResource in my service definition:
angular.module('periodservice',['ngResource']).
factory('Periods', function($resource) {
return $resource('../rest/periods', {}, {
query : {method : 'GET', params : {}, isArray : true}
});
});
The controller is pretty straight forward.
function EksternVareRedigeringCtrl ($scope, Periods){
$scope.periods = Periods.query(function(success, status, headers){
$scope.msgf = success;
console.log("this is the succsess);
}, function(errors){
$scope.msgf = errors;
console.log("This is an error....." + ' ' + $scope.msgf.status);
});
}
Now I display the date like this instead of converting it to a proper date:
<div class="period">Period: {{period[0]}}-{{period[1]}}-{{period[2]}}</div>
Convert to Javascript Date:
new Date(period.startDate[0], period.startDate[1]-1, period.startDate[2])
Note that I've used period.startDate[1]-1 because the months start at 0 (January) and end at 11 (December).
To process the dates only once, add the following snippet to your success callback:
angular.forEach($scope.periods, function (period) {
period.startDate = new Date(period.startDate[0], period.startDate[1]-1, period.startDate[2]);
});
Thanks a lot. It works perfectly!
Note. You do not have to subtract -1 from month number when supplying an array to new Date():
new Date([2012, 12, 24]) === new Date(2012, 11, 24);