Introduction
I have recently been working on a JavaScript project where I needed to sort Dates by descending order and Time by ascending order. For this project, I am already using Moment.js as my Date
Time JavaScript Library. The aggregate I am sorting contains moment objects and not native Date
JavaScript objects.
Sorting by the JavaScript Date
object is easy enough, but I have not found many methods on sorting by Date
and Time
independently. Therefore, the goal of this post is to come up with a solution for sorting by descending Date
and ascending Time
.
Sort by Date
In JavaScript, sorting by Date
and Time
in either ascending or descending order is pretty much built in through the Date
’s greater than ( > ) and less than ( < ) operators.
Example 1
var
momentDates = […],
sortByDateAsc = function (lhs, rhs) { return lhs > rhs ? 1 : lhs < rhs ? -1 : 0; },
sortByDateDesc = function (lhs, rhs) { return lhs < rhs ? 1 : lhs > rhs ? -1 : 0; },
momentDates.sort(sortByDateAsc);
momentDates.sort(sortByDateDesc);
Sort by Time
With JavaScript alone, I have not found a quick and easy solution to sort specifically by time; therefore, we need to create one.
I am going to breakdown the hours, minutes and seconds, then compare them individually. I am going to start with comparing hours first and only if the hours are identical, I will compare minutes. If minutes are identical, I will finally compare seconds. One could easily go as precise as milliseconds, but that level of accuracy is not necessary for my needs.
Example 2
var
sortByTimeAsc = function (lhs, rhs) {
var results;
results = lhs.hours() > rhs.hours() ? 1 : lhs.hours() < rhs.hours() ? -1 : 0;
if (results === 0)
results = lhs.minutes() > rhs.minutes() ? 1 : lhs.minutes() < rhs.minutes() ? -1 : 0;
if (results === 0)
results = lhs.seconds() > rhs.seconds() ? 1 : lhs.seconds() < rhs.seconds() ? -1 : 0;
return results;
},
sortByTimeDesc = function (lhs, rhs) {
var results;
results = lhs.hours() < rhs.hours() ? 1 : lhs.hours() > rhs.hours() ? -1 : 0;
if (results === 0)
results = lhs.minutes() < rhs.minutes() ? 1 : lhs.minutes() > rhs.minutes() ? -1 : 0;
if (results === 0)
results = lhs.seconds() < rhs.seconds() ? 1 : lhs.seconds() > rhs.seconds() ? -1 : 0;
return results;
};
momentDates.sort(sortByTimeAsc);
momentDates.sort(sortByTimeDesc);
Sort by Descending Date and Ascending Time
It would be nice to simply combine both of these algorithms into a nice easy to use function:
Example 3
sortByDateDescAndTimeAsc = function (lhs, rhs) {
var results = sortByDateDesc(lhs,rhs);
if (results === 0)
results = sortByTimeDesc(lhs, rhs);
return results;
},
Unfortunately, as I previously stated, the greater than ( > ) and less than ( < ) operators sort by Date
and Time
; Therefore, this approach would not work because sortByDateDesc
would only ever return “0
” if both of the Date
and Time
are identical, effectively bypassing or uselessly executing the Time
sorting portion of the code.
I have decided we could segregate the Date
portion into Years
, Months
and Days
, similarly to how we sorted Times
(e.g., If the Years
, Months
and Days
are identical, then compare the Time
portion as we previously did).
Example 4
sortByDateDescAndTimeAsc = function (lhs, rhs) {
var results;
results = lhs.years() < rhs.years() ? 1 : lhs.years() > rhs.years() ? -1 : 0;
if (results === 0)
results = lhs.months() < rhs.months() ? 1 : lhs.months() > rhs.months() ? -1 : 0;
if (results === 0)
results = lhs.date() < rhs.date() ? 1 : lhs.date() > rhs.date() ? -1 : 0;
if (results === 0)
results = lhs.hours() > rhs.hours() ? 1 : lhs.hours() < rhs.hours() ? -1 : 0;
if (results === 0)
results = lhs.minutes() > rhs.minutes() ? 1 : lhs.minutes() < rhs.minutes() ? -1 : 0;
if (results === 0)
results = lhs.seconds() > rhs.seconds() ? 1 : lhs.seconds() < rhs.seconds() ? -1 : 0;
return results;
},
Conclusion
By breaking down this problem, we were easily able to create a solution that works. But, I know you are not using Moment.js and you don’t want to use Moment.js just to sort Dates. Well, Moment.js is simply a wrapper, with amazing features, around a native Date
object. You can easily alter my provided examples and use the Date
object to accomplish the exact same thing. The only difference is instead of invoking the method seconds on a moment.js object, you would invoke getSeconds
on a Date
object.
To quickly generate the native JavaScript code, skillfully write a macro to find and replace (i.e., Find: hs.Replace: hs.get
), capitalize the method names and perform a couple other small tweaks.
Example 5
sortByDateDescAndTimeAscDateObj = function (lhs, rhs) {
var results;
results = lhs.getYear() < rhs.getYear() ?
1 : lhs.getYear() > rhs.getYear() ? -1 : 0;
if (results === 0)
results = lhs.getMonth() < rhs.getMonth() ?
1 : lhs.getMonth() > rhs.getMonth() ? -1 : 0;
if (results === 0)
results = lhs.getDate() < rhs.getDate() ?
1 : lhs.getDate() > rhs.getDate() ? -1 : 0;
if (results === 0)
results = lhs.getHours() > rhs.getHours() ?
1 : lhs.getHours() < rhs.getHours() ? -1 : 0;
if (results === 0)
results = lhs.getMinutes() > rhs.getMinutes() ?
1 : lhs.getMinutes() < rhs.getMinutes() ? -1 : 0;
if (results === 0)
results = lhs.getSeconds() > rhs.getSeconds() ?
1 : lhs.getSeconds() < rhs.getSeconds() ? -1 : 0;
return results;
},
I hope this can be of value to someone. You can find another example of the code here.