3

I have a json data similar like below. I am trying to sort it using the below code

function comp(a, b) {

    return new Date(a.event_date) - new Date(b.event_date);
}

data=data.sort(comp)    

But the problem is two events can be on the same dates but on different times which is another element in the json data called event_time as can be seen on the same json data.

How to sort such that firstly it sorts by event_date and if these are equal, then sorts by their respective times ?

Thanks in advance?

{
    id: "xxxx",
    feed_id: "yyyy",
    title: "abcd ",
    detail: "efgh.",
    event_date: "Tue, 26 May 2015 00:00:00 +1000",
    event_time: "6:30pm",
    date: "Thu, 23 Apr 2015 23:05:04 +1000",
    expires_at: 1432634400,
    end_time: "8:00pm",
    timestamp: 1429794304
},
{
    id: "xxxx",
    feed_id: "yyyy",
    title: "efgh",
    detail: "efgh.",
    event_date: "Tue, 26 May 2015 00:00:00 +1000",
    event_time: "4:30pm",
    date: "Thu, 23 Apr 2015 23:05:04 +1000",
    expires_at: 1432634400,
    end_time: "8:00pm",
    timestamp: 1429794304
},    {
    id: "xxxx",
    feed_id: "yyyy",
    title: "ijkl",
    detail: "efgh.",
    event_date: "Tue, 27 May 2015 00:00:00 +1000",
    event_time: "1:30pm",
    date: "Thu, 23 Apr 2015 23:05:04 +1000",
    expires_at: 1432634400,
    end_time: "8:00pm",
    timestamp: 1429794304
}
Hello Universe
  • 3,248
  • 7
  • 50
  • 86
  • 1
    Why don't you put yopur event_time inside event_date, instead of 00:00:00? – jcubic Apr 25 '15 at 10:18
  • That would be the obvious choice. However the jsonp data that I am working on actually is coming from a third party.. so no luck there – Hello Universe Apr 25 '15 at 10:18
  • Take a look at underscore/lodash sorting and grouping functions. https://stackoverflow.com/questions/10238127/sorting-by-date-with-underscore-js-or-just-plain-js/10238576 – Fini Apr 25 '15 at 11:11

6 Answers6

4

Here's a simple solution:

function comp(a, b) {
  var d1= new Date(a.event_date)*1,
      d2= new Date(b.event_date)*1,
      t1= new Date('1/1/1970 '+(a.event_time.replace(/(a|p)/i, ' $1')))*1,
      t2= new Date('1/1/1970 '+(b.event_time.replace(/(a|p)/i, ' $1')))*1;

  return (d1+t1)-(d2+t2);
} //comp

This code:

  1. Inserts a space before "pm" or "am" to make the times valid.
  2. Appends each time to the date 1/1/1970 and converts that to a Date.
  3. Makes all dates numeric (number of milliseconds since 1/1/1970).
  4. Subtracts the sum of the b.event_date and b.event_time (as a date) from the sum of the a.event_date and a.event_time (as a date).

It works regardless of the case of "pm" or "am", or the number of spaces (0 or more) before "pm" or "am." If "pm" or "am" are missing, it assumes 24-hour military time.

Fiddle

Rick Hitchcock
  • 35,202
  • 5
  • 48
  • 79
3

If subtracting the two dates results in 0 (no difference), include the time in the equation. Something like:

var result = document.querySelector('#result');
result.textContent = 
   JSON.stringify(getData().sort(sortDateTime), null, ' ') +
   '\n\n**using sortDateTime2\n'+
   JSON.stringify(getData().sort(sortDateTime2), null, ' ');



function sortDateTime(a, b) {
  var A = new Date(a.event_date);
  var B = new Date(b.event_date);
  if (A - B == 0) {
    // no difference, include event_time
    var tA = a.event_time.split(':');
    var tB = b.event_time.split(':');
    A.setHours( /pm/i.test(tA[1]) ? +tA[0]+12 : tA[0] );
    A.setMinutes(+tA[1].replace(/[a-z]/gi, ''));
    B.setHours(/pm/i.test(tB[1]) ? +tB[0]+12 : tB[0]);
    B.setMinutes(+tB[1].replace(/[a-z]/gi, ''));
  }
  return A - B;
}

// sorting on date/time using setTime helper
function sortDateTime2(a, b) {
  return setTime(new Date(a.event_date), a.event_time) - 
         setTime(new Date(b.event_date), b.event_time);
}

// bonus: helper to set time from a time string
function setTime(thisDate, timeStr) {
  return new Date(
           [ [ thisDate.getFullYear(), 
               thisDate.getMonth()+1, 
               thisDate.getDate() 
             ].join('/'),
             timeStr.replace(/(a|p)/, function (m) {return ' ' + m;} ) ]
             .join(' ')
  );
}

function getData() {
  return [{
     id: "xxxx",
     feed_id: "yyyy",
     title: "abcd ",
     detail: "efgh.",
     event_date: "Tue, 26 May 2015 00:00:00 +1000",
     event_time: "6:30pm",
     date: "Thu, 23 Apr 2015 23:05:04 +1000",
     expires_at: 1432634400,
     end_time: "8:00pm",
     timestamp: 1429794304
    },
    {
     id: "xxxx",
     feed_id: "yyyy",
     title: "abcd ",
     detail: "efgh.",
     event_date: "Tue, 26 May 2015 00:00:00 +1000",
     event_time: "4:30pm",
     date: "Thu, 23 Apr 2015 23:05:04 +1000",
     expires_at: 1432634400,
     end_time: "8:00pm",
     timestamp: 1429794304
    },    {
     id: "xxxx",
     feed_id: "yyyy",
     title: "abcd ",
     detail: "efgh.",
     event_date: "Tue, 27 May 2015 00:00:00 +1000",
     event_time: "1:30pm",
     date: "Thu, 23 Apr 2015 23:05:04 +1000",
     expires_at: 1432634400,
     end_time: "8:00pm",
     timestamp: 1429794304
    }
  ];
}
<pre id="result"></pre>
KooiInc
  • 119,216
  • 31
  • 141
  • 177
  • Thanks @KooiInc . I ended up using your sortDateTime function to sort my json data. However, if both date and time comes out with no difference that is both events at same date and time, is it possible to extend this function to sort then by title. That is, if both date and time are same, then sort by title? – Hello Universe Apr 26 '15 at 02:20
  • 1
    Hi @Welcome, ofcourse it's *possible* (in the sort delegate check `dataA == dateB` and if so, take `title` in consideration). But I would advice against that. Sorting on `title` if datetime values are equal within the date(time)-sort delegate introduces ambiguity. What would be more comprehensible imho, is to *first* sort on date(time), *then* sort on `title`. So, something like `[...].sort(datetimeSortDelegate).sort(titleSortDelegate)` – KooiInc Apr 26 '15 at 08:00
2

I don't think that Date.parse works for your time format. You can use something like this:

function parseDateTime(d, t) {
  var date = new Date(d);
  var a = t.slice(-2);
  var t = +t.replace(/[apm:]/g, '');
  var h = ~~(t/100);
  var m = t%100;

  if(a == "pm" && h < 12) h += 12;
  if(a == "am" && h == 12) h -= 12;
  date.setHours(h);
  date.setMinutes(m);
  return date;
}

function comp(a, b) {
  var da = parseDateTime(a.event_date, a.event_time);
  var db = parseDateTime(b.event_date, b.event_time);

  return da - db;
}

function parseTime(t) {
  var a = t.slice(-2);
  var t = +t.replace(/[apm:]/g, '');

  if(a == "pm" && ~~(t/100) < 12) t += 1200;
  if(a == "am" && ~~(t/100) == 12) t -= 1200;
  return t;
}

console.log(Date.parse('8:00pm'));
// NaN

console.log(parseTime('8:00am'));
console.log(parseTime('11:59am'));
console.log(parseTime('12:00pm'));
console.log(parseTime('12:01pm'));
// 800
// 1159
// 1200
// 1201

console.log(parseTime('8:00pm'));
console.log(parseTime('11:59pm'));
console.log(parseTime('12:00am'));
console.log(parseTime('12:01am'));
// 2000
// 2359
// 0
// 1
dting
  • 38,604
  • 10
  • 95
  • 114
1

You could use this :

function comp(a, b) {
    // Compare dates
    if (new Date(a.event_date).getTime() != new Date(b.event_date).getTime())
        return new Date(a.event_date) - new Date(b.event_date);

    // If dates are equal, compare hours
    return Date.parse(a.event_time) - Date.parse(b.event_time);
}

data=data.sort(comp)  

EDIT I added the custom date parser (inspired by this) and the string sorter.

function parseDate(dateString) {
    var d = new Date();
    var time = dateString.match(/(\d+)(?::(\d\d))?\s*(p?)/);
    d.setHours( parseInt(time[1]) + (time[3] ? 12 : 0) );
    d.setMinutes( parseInt(time[2]) || 0 );
    return d;
}

function comp(a, b) {
    // Compare dates
    if (new Date(a.event_date).getTime() != new Date(b.event_date).getTime())
        return new Date(a.event_date) - new Date(b.event_date);

    // If dates are equal, compare hours
    var hourA = parseDate(a.event_time);
    var hourB = parseDate(b.event_time);
    if (hourA.getTime() != hourB.getTime())
        return hourA - hourB;

    // If hours are equal, compare titles
    if(a.title > b.title)
        return -1;
    else
        return 1;
}
Community
  • 1
  • 1
Harijoe
  • 1,771
  • 1
  • 16
  • 27
1

You can use moment.js to create date objects for event_time

var sortedJ = _j.sort(function(a,b){

    var result = new Date(a.event_date) - new Date(b.event_date);

    if(result === 0)
    {
        return new moment("01/01/2000 "+a.event_time,"MM/DD/YYYY h:mm:ss a") - moment("01/01/2000 "+b.event_time,"MM/DD/YYYY h:mm:ss a");
    }

    return result;
});

Here is a working JS FIDDLE

Sameer Azazi
  • 1,503
  • 10
  • 16
0

You could try this

function comp(a, b) {
    var diff = new Date(a.event_date) - new Date(b.event_date);
    if(diff < 0) return -1;
    else if(diff > 0) return 1;
    else if(parseTime(a.event_time) < parseTime(b.event_time)) return -1;
    else if(parseTime(a.event_time) > parseTime(b.event_time)) return 1;
    else return 0;
}

But why not make event_date and event_time one single property?

edit:

You could also define yourself a sortBy function to help you sort by any arbitrary number of properties.

function sortBy(list, f) {
  list.sort(function(a, b) {
    var fa = f(a), fb = f(b);
    if(fa < fb) return -1;
    else if(fa > fb) return 1;
    else return 0;
  });
}

Then you can sort by date, time, and title like this

sortBy(list, function(o) {
  return [new Date(o.event_date), parseTime(o.event_time), o.title];
});
Tesseract
  • 8,049
  • 2
  • 20
  • 37
  • 1
    event_time is a string you can't compare strings like that. – jcubic Apr 25 '15 at 10:28
  • What is parseTime? In chromium it's undfined. Also you can't compare arrays like that. – jcubic Apr 25 '15 at 11:10
  • Yes, you can compare arrays like that. I tested it in Chrome and Firefox. Also parseTime was already posted by DTing so I figured I don't have to post it myself. – Tesseract Apr 25 '15 at 11:14