2
votes

Given this data, my sort function will sort by ascending date, ascending time, and in alphabetical order by first name:

[
  ['Kurt Asdf', '25 Nov 2017 4:30 PM'],
  ['Vincent Qwerty', '25 Nov 2017 4:30 PM'],
  ['Zed Jones', '24 Nov 2017 2:00 PM'],
  ['Jack Mo', '25 Nov 2017 5:00 PM'],
  ['John Phil', '25 Nov 2017 4:00 PM'],
  ['Bob Phil', '25 Nov 2017 4:00 PM']
]

Here is the output from my current sort function (code below):

[
  ['Zed Jones', '24 Nov 2017 2:00 PM'],
  ['Bob Phil', '25 Nov 2017 4:00 PM'],
  ['John Phil', '25 Nov 2017 4:00 PM'],
  ['Kurt Asdf', '25 Nov 2017 4:30 PM'],
  ['Vincent Qwerty', '25 Nov 2017 4:30 PM'],
  ['Jack Mo', '25 Nov 2017 5:00 PM']
]

Here is my desired output:

[
  ['Bob Phil', '25 Nov 2017 4:00 PM'],
  ['John Phil', '25 Nov 2017 4:00 PM'],
  ['Kurt Asdf', '25 Nov 2017 4:30 PM'],
  ['Vincent Qwerty', '25 Nov 2017 4:30 PM'],
  ['Jack Mo', '25 Nov 2017 5:00 PM'],
  ['Zed Jones', '24 Nov 2017 2:00 PM']
]

Notice how in the above output the dates are in descending order yet the times are in ascending order and the alphabetical order within each time slot is maintained.

Here is what I have tried:

function sortTable(data) {
  return data.sort((elem1, elem2) => {
    var dateA         = new Date(elem1[1])
      , dateB         = new Date(elem2[1])
      , nameA         = elem1[0]
      , nameB         = elem2[0]
      , datecomp      = dateB-dateA;

    if (nameA === undefined || nameB === undefined)
      namecomp = 0;
    else
      namecomp = nameA[0] > nameB[0] || -(nameA[0] < nameB[0]);

    return datecomp > 0 ? datecomp : datecomp + namecomp;
  });
}
2
Well you'll have to separate date and time first of all, and them compare time as well after you found that dates are equal. Can't see how your code is supposed to work btw.; what is elem1[5] when the elements you are passing in seem to be arrays with two entries? - CBroe
Ascending of Date , or Descending , whatever you wanna do just fix one parameter, Time , Date and name are related , So one parameter should be specify , you can not do ascending or descending on all. - Muhammad Ali

2 Answers

2
votes

You could use a chained approach for sorting parts in different sort order.

I suggest to use a single function for comparing and returning the order.

You could use an ISO 8601 string for sorting date descending and time ascending. Later sort by name ascending.

Why taking an ISO 8601 date/time string?

The conversion of

25 Nov 2017 4:30 PM

returns a sortable string in the format of

2017-11-25T15:30:00.000Z ISO in GMT
^^^^^^^^^^               date
           ^^^^^^^^      time without milli seconds

From this we take the date part for sorting descending

2017-11-25

and the time part

15:30:00

for sorting ascending, if the date is the same.

More to read here: Sort ISO 8601 dates forward or backwards.

var array = [['Kurt Asdf', '25 Nov 2017 4:30 PM'], ['Vincent Qwerty', '25 Nov 2017 4:30 PM'], ['Zed Jones', '24 Nov 2017 2:00 PM'], ['Jack Mo', '25 Nov 2017 5:00 PM'], ['John Phil', '25 Nov 2017 4:00 PM'], ['Bob Phil', '25 Nov 2017 4:00 PM']];

array.sort(function (a, b) {
    function compare(a, b) {
        return a > b || -(a < b);
    }

    var aISO = new Date(a[1]).toISOString(),
        bISO = new Date(b[1]).toISOString();

    return compare(bISO.slice(0, 10), aISO.slice(0, 10))   // date desc
        || compare(aISO.slice(11, 19), bISO.slice(11, 19)) // time asc
        || compare(a[0], b[0]);                            // name asc
});

console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
1
votes

My suggestion to keep things readable is to split the date into day and time in order to compare them separately :

// number of milliseconds in a day = 24 * 3600 * 1000 = 24 * 36e5
millis = date.getTime()
time = millis % (24 * 36e5)
day = millis - time

JavaScript dates precision is in milliseconds, thus, date.getTime() gives the number of elapsed milliseconds since 1970-01-01. Here is what I would do :

var dates = [
  new Date(2017, 0, 1, 0, 0, 1),
  new Date(2017, 0, 2, 0, 0, 2),
  new Date(2017, 0, 3, 0, 0, 1),
  new Date(2017, 0, 2, 0, 0, 1),
  new Date(2017, 0, 3, 0, 0, 2),
  new Date(2017, 0, 1, 0, 0, 2)
];

dates.sort(function (date1, date2) {
  var millisInADay = 24 * 36e5;
  var millis1 = date1.getTime();
  var millis2 = date2.getTime();
  var time1 = millis1 % millisInADay;
  var time2 = millis2 % millisInADay;
  var day1 = millis1 - time1;
  var day2 = millis2 - time2;
  if (day1 < day2) return 1;
  if (day1 > day2) return -1;
  if (time1 > time2) return 1;
  if (time1 < time2) return -1;
  return 0;
});

dates.forEach(function (date) {
  console.log(date.toLocaleString());
});

Here is the result using your own data table :

var table = [
  ['Kurt Asdf', '25 Nov 2017 4:30 PM'],
  ['Vincent Qwerty', '25 Nov 2017 4:30 PM'],
  ['Zed Jones', '24 Nov 2017 2:00 PM'],
  ['Jack Mo', '25 Nov 2017 5:00 PM'],
  ['John Phil', '25 Nov 2017 4:00 PM'],
  ['Bob Phil', '25 Nov 2017 4:00 PM']
];

table.sort(function (x1, x2) {
  var millisInADay = 24 * 36e5;
  var name1 = x1[0];
  var name2 = x2[0];
  var date1 = new Date(x1[1]);
  var date2 = new Date(x2[1]);
  var millis1 = date1.getTime();
  var millis2 = date2.getTime();
  var time1 = millis1 % millisInADay;
  var time2 = millis2 % millisInADay;
  var day1 = millis1 - time1;
  var day2 = millis2 - time2;
  if (day1 < day2) return 1;
  if (day1 > day2) return -1;
  if (time1 > time2) return 1;
  if (time1 < time2) return -1;
  if (name1 > name2) return 1;
  if (name1 < name2) return -1;
  return 0;
});

table.forEach(function (x) {
  var date = new Date(x[1]);
  console.log(date.toLocaleString(), "|", x[0]);
});