3

How to return the lowest date value and highest date value from an array?

For example for the below array, I'd like to create a function that returns the lowest date, as well as another function that returns the highest date?

var data = [
  {date: "2011-11-01T16:17:54Z", quantity: 2, total: 190, tip: 100, type: "tab"},
  {date: "2011-11-14T16:20:19Z", quantity: 2, total: 190, tip: 100, type: "tab"},
  {date: "2011-11-14T16:28:54Z", quantity: 1, total: 300, tip: 200, type: "visa"},
  {date: "2011-11-14T16:30:43Z", quantity: 2, total: 90, tip: 0, type: "tab"},
  {date: "2011-11-14T16:48:46Z", quantity: 2, total: 90, tip: 0, type: "tab"},
  {date: "2011-11-14T16:53:41Z", quantity: 2, total: 90, tip: 0, type: "tab"},
  {date: "2011-11-14T16:54:06Z", quantity: 1, total: 100, tip: 0, type: "cash"},
  {date: "2011-11-14T16:58:03Z", quantity: 2, total: 90, tip: 0, type: "tab"},
  {date: "2011-11-14T17:07:21Z", quantity: 2, total: 90, tip: 0, type: "tab"},
  {date: "2011-11-14T17:22:59Z", quantity: 2, total: 90, tip: 0, type: "tab"},
  {date: "2011-11-14T17:25:45Z", quantity: 2, total: 200, tip: 0, type: "cash"},
  {date: "2011-11-31T17:29:52Z", quantity: 1, total: 200, tip: 100, type: "visa"}
]; 

I'm not sure if this is relevant, but I am using this array with crossfilter, so I'm not sure if crossfilter has any helpful methods.

Chris
  • 4,194
  • 9
  • 52
  • 100
  • 1
    Is the array always ordered like that? Becuase than you could simply return the first or the las – Vincent Beltman Nov 04 '14 at 13:07
  • 2
    That last date doesn't convert to a date properly. – Andy Nov 04 '14 at 13:10
  • You can sort the array and then get the upper and lower bounds. Take a look at this answer: **[Sort Javascript Object Array By Date](http://stackoverflow.com/a/26759127/2247494)** – jherax Mar 30 '15 at 23:24

6 Answers6

6

We just have to apply some Javascript Magic.

var result = Math.max.apply( null, data.map(function( v ) {
    return +new Date( v.date );
}));

There, we map those Objects into just their timestamp representation from the ISO-Date string. Then we simply apply a Math.max, respectively Math.min on the resulting Array. All we have left to do now, is to re-convert that timestamp into a Date object

new Date( result );

And just for the heck of it, an alternative solution using Array.prototype.reduce and pure String comparison (if you are scared of browser incompatibilities on parsing ISO Date Strings).

var max = data.reduce(function( prev, current ) {
  return prev.date > current.date ? prev : current;
});
// for the min version just ">" to "<"
jAndy
  • 212,463
  • 51
  • 293
  • 348
  • @Andy I realized myself right now, last entry seems a little off for some reason. – jAndy Nov 04 '14 at 13:11
  • A good answer. However, a simple string comparison within a loop could also give the OP's expected result. –  Nov 04 '14 at 13:11
  • [Remove the last date and it works](http://jsfiddle.net/tLps8f1p/). I like that neat trick of coercing the date to milliseconds too. – Andy Nov 04 '14 at 13:11
  • 1
    @Andy I bet you like the upgraded Magic aswell! – jAndy Nov 04 '14 at 13:17
  • @jAndy, what Magic is that? – Andy Nov 04 '14 at 13:18
  • "We" don't ever use the Date constructor to parse date strings due to the inherent unreliability of doing so (e.g. IE 8 will return NaN when given the format specified in the ES5 specification). ISO 8601 date strings sort as expected anyway, there is no need to convert to a Date at all. – RobG Nov 04 '14 at 13:38
  • Well "I" don't really care about IE8 anymore and if `Math.max` is used like shown above, there is a need to convert. – jAndy Nov 04 '14 at 13:43
  • You're *sure* IE8 is the only one? How many phone browsers have you tested? TVs? Other devices? According to [*netmarketshare*](http://netmarketshare.com), IE 8 still has over 17% desktop marketshare. The point being, the strings can be sorted successfully and reliably in all browsers without converting to a date. – RobG Nov 04 '14 at 13:49
  • @RobG how many browsers did you test? Why so much hate? I only care about that it is specced by W3 and based on this fact I'm giving answers. – jAndy Nov 04 '14 at 13:54
  • However, based on your demur, I tweaked the `.reduce` version into string comparison. I'm wondering why it seems to work properly using `` operators, but not with `.localeCompare()` String.prototype method, any clue ? – jAndy Nov 04 '14 at 13:59
  • @jAndy—there is no hate. There are many browsers that don't fully support the latest W3C standards, not accommodating well known non–compliance isn't a good idea. The inconsistencies are very much fewer in the latest browsers, but not everyone uses them and new ones are emerging all the time (and standards are constantly changing, including how ISO date strings are parsed in the ES6 draft). – RobG Nov 04 '14 at 14:01
  • @jAndy—the result of [*localCompare*](http://ecma-international.org/ecma-262/5.1/#sec-15.5.4.9) is implementation dependent, I don't know how it works internally. However, `return a.date.localeCompare(b.date);` seems to work. – RobG Nov 04 '14 at 14:07
  • @RobG interesting. How certain are you about the browser compatibility on comparing (Date)Strings using comparison operators ? – jAndy Nov 04 '14 at 14:15
  • @jAndy—the behaviour of the [*The Abstract Relational Comparison Algorithm*](http://ecma-international.org/ecma-262/5.1/#sec-11.8.5) used for the [*less–than*](http://ecma-international.org/ecma-262/5.1/#sec-11.8.1) and [*greater–than*](http://ecma-international.org/ecma-262/5.1/#sec-11.8.2) operators is specified precisely, so it will be consistent in conforming implementations. It's possible that there are non–conforming implementations in use, but that's extremely unlikely (though not impossible) given the algorithm has been part of the language since the beginning. – RobG Nov 05 '14 at 00:20
1

You can sort the array using a custom comparator:

data.sort(function(o1,o2){
    return new Date(o1.date).getTime() - new Date(o2.date).getTime();
});

and then get the largest/smallest dates by getting the first/last items:

var smallest = data[0].date;
var largest  = data[data.length - 1].date;
codebox
  • 18,210
  • 7
  • 54
  • 77
  • Another approach sorting the array: **[Sort Javascript Object Array By Date](http://stackoverflow.com/a/26759127/2247494)** – jherax Mar 30 '15 at 23:26
1

A simple string comparison should give you the expected result. Since the dates are in ISO string format, this should be fairly easy to do.

var lowestIndex = 0;
var lowestDate = data[0].date;
for (var i=0; i<data.length; i++) {
    if (lowestDate > data[i].date) {
        lowestDate = data[i].date;
        lowestIndex = i;
    }
}
return data[lowestIndex];

If you are comfortable with apply and call, then you shouldn't have much trouble converting this simple logic to a function.

0

You could sort by those keys, and then take the first and last values:

sortedData = data.sort(function(a, b) {
    return new Date(a.date) > new Date(b.date) ? 1 : -1;
});
var min = sortedData[0],
    max = sortedData[sortedData.length - 1];
tckmn
  • 52,184
  • 22
  • 101
  • 145
0

Try this:

var data = [
  {date: "2011-11-01T16:17:54Z", quantity: 2, total: 190, tip: 100, type: "tab"},
  {date: "2011-11-14T16:20:19Z", quantity: 2, total: 190, tip: 100, type: "tab"},
  {date: "2011-11-14T16:28:54Z", quantity: 1, total: 300, tip: 200, type: "visa"},
  {date: "2011-11-14T16:30:43Z", quantity: 2, total: 90, tip: 0, type: "tab"},
  {date: "2011-11-14T16:48:46Z", quantity: 2, total: 90, tip: 0, type: "tab"},
  {date: "2011-11-14T16:53:41Z", quantity: 2, total: 90, tip: 0, type: "tab"},
  {date: "2011-11-14T16:54:06Z", quantity: 1, total: 100, tip: 0, type: "cash"},
  {date: "2011-11-14T16:58:03Z", quantity: 2, total: 90, tip: 0, type: "tab"},
  {date: "2011-11-14T17:07:21Z", quantity: 2, total: 90, tip: 0, type: "tab"},
  {date: "2011-11-14T17:22:59Z", quantity: 2, total: 90, tip: 0, type: "tab"},
  {date: "2011-11-14T17:25:45Z", quantity: 2, total: 200, tip: 0, type: "cash"},
  {date: "2011-11-31T17:29:52Z", quantity: 1, total: 200, tip: 100, type: "visa"}
];

var lowest = data.reduce(function (a, b) {
    var timeA = new Date(a.date).getTime();
    var timeB = new Date(b.date).getTime();
    return timeA < timeB ? a : b;
});

alert("lowest: " + JSON.stringify(lowest, null, 4));

var highest = data.reduce(function (a, b) {
    var timeA = new Date(a.date).getTime();
    var timeB = new Date(b.date).getTime();
    return timeA > timeB ? a : b;
});

alert("highest: " + JSON.stringify(highest, null, 4));
Aadit M Shah
  • 67,342
  • 26
  • 146
  • 271
0

Lots of suggestions to convert the strings to dates, however using the Date constructor to parse strings is unreliable and will fail, even for the format specified in ES5, for a reasonable number of browsers in use.

Since you are using ISO 8601 format date strings, you can sort them without conversion (that's one of the benefits of using that format, as well as its being unambiguous). So you can sort the array using:

data.sort(function(a, b) {return a.date > b.date? 1 : a.date < b.date? -1 : 0});

Now you just get the first and last member of the array:

var earliestDate = data[0].date;
var latestDate   = data[data.length - 1].date;

As others have said, the last date string is an invalid date, how should the sort deal with that? The above suggestion doesn't test whether the date is valid or not, it just looks at the characters.

RobG
  • 124,520
  • 28
  • 153
  • 188
  • ok but sorting all dates to get only the lowest and highest is not the best for efficiency – Joel Nov 04 '14 at 16:09