120

I use angularjs in project.

I get array of objects from the server. Each object contains few properties and one of them is date property.

Here is the Array (in json) that I get from server:

[
  {
    "Address": 25,
    "AlertType": 1,
    "Area": "North",
    "MeasureDate": "2019-02-01T00:01:01.001Z",
    "MeasureValue": -1
  },
  {
    "Address": 26,
    "AlertType": 1,
    "Area": "West",
    "MeasureDate": "2016-04-12T15:13:11.733Z",
    "MeasureValue": -1
  },
  {
    "Address": 25,
    "AlertType": 1,
    "Area": "North",
    "MeasureDate": "2017-02-01T00:01:01.001Z",
    "MeasureValue": -1
  }
          .
          .
          .
]

I need to get the latest date from the array.

What is the elegant way to get the latest date from array of objects?

Astaroth
  • 763
  • 6
  • 25
Michael
  • 13,950
  • 57
  • 145
  • 288
  • Loop over and find the latest date. – epascarello Apr 12 '16 at 15:08
  • @epascarello, do i have to convert it to Date javascript object. – Michael Apr 12 '16 at 15:25
  • Yes you would need to convert the string to a date. – epascarello Apr 12 '16 at 15:29
  • You don't need to convert it. It can be ordered as is. And, in fact, there are some arguments to say introducing conversion opens the floor for introducing more potential areas for issues to appear (parsing etc.). –  Apr 12 '16 at 16:24
  • You don't have to convert it because of the specific format you have chosen. I'd argue that it's better to convert though, since if your application ever uses a different format or accepts multiple formats then this could break whereas parsing as a date and then ordering ensures that it truly is the latest date (which may not necessarily be the last ordered string). For example, if you start accepting MM/DD/YYYY dates, then string order could be drastically different from date order. – arcyqwerty Apr 12 '16 at 19:10

11 Answers11

143

A clean way to do it would be to convert each date to a Date() and take the max

ES6:

new Date(Math.max(...a.map(e => new Date(e.MeasureDate))));

JS:

new Date(Math.max.apply(null, a.map(function(e) {
  return new Date(e.MeasureDate);
})));

where a is the array of objects.

What this does is map each of the objects in the array to a date created with the value of MeasureDate. This mapped array is then applied to the Math.max function to get the latest date and the result is converted to a date.

By mapping the string dates to JS Date objects, you end up using a solution like Min/Max of dates in an array?

--

A less clean solution would be to simply map the objects to the value of MeasureDate and sort the array of strings. This only works because of the particular date format you are using.

a.map(function(e) { return e.MeasureDate; }).sort().reverse()[0]

If performance is a concern, you may want to reduce the array to get the maximum instead of using sort and reverse.

arcyqwerty
  • 10,325
  • 4
  • 47
  • 84
  • interesting, spicy solutions. Didn't know that you can apply date objects to Math.max immediately. – Anton Harald Apr 12 '16 at 16:33
  • 10
    ES6: new Date(Math.max(...a.map(a=> new Date(a.MeasureDate)))); – ricka Sep 25 '17 at 18:31
  • 6
    How would I get the whole object back instead of just the date? – Travis Heeter Feb 11 '19 at 15:16
  • 3
    There's probably an easier way to do this, but I had to write another function to get the object: So your function gives a Date, and let's say that's assigned to the var `mostRecentDate`. Then I can do this to get the object: `var mostRecentObject = a.filter( e => { var d = new Date( e.MeasureDate ); return d.getTime() == mostRecentDate.getTime();})[0];` – Travis Heeter Feb 11 '19 at 21:28
  • You are a legend – Immortal Jul 04 '19 at 03:18
  • i have a slightly complicated scenario, what if the JSON (address) need to compare against another JSON (employment array) with effective date too, then would need to find out effective employee hired (year 2016 Apr), it should return max as of 2016 Apr (stay in 'West'), upon promotion (year 2020 May), it should return max address as of 2019 Feb in address JSON row above, stay in North instead. How to achieve as there is a employment date for comparison. – Weilies Sep 10 '20 at 02:24
  • e.MeasureDate ? new Date(e.MeasureDate) : '' – ykay says Reinstate Monica Jan 11 '21 at 11:30
  • 2
    For Typescript, you have to convert the dates to a number first - `new Date(Math.max(...a.map(e => new Date(e.MeasureDate).getTime())));` – Yasha Feb 14 '23 at 12:17
77

Further to @Travis Heeter's answer, this returns the object that contains the latest date:

array.reduce((a, b) => (a.MeasureDate > b.MeasureDate ? a : b));

A more robust solution perhaps might be convert the strings into Date objects every time. Could be noticeably slower if dealing with (very) large arrays:

array.reduce((a, b) => {
  return new Date(a.MeasureDate) > new Date(b.MeasureDate) ? a : b;
});
Yorkshireman
  • 2,194
  • 1
  • 21
  • 36
  • 1
    Here's the one liner: array.reduce((a, b) => new Date(a.MeasureDate) > new Date(b.MeasureDate) ? a : b) – DSLuminary Apr 02 '20 at 15:47
  • 7
    personally I think the one liner is a bit too long, which is why I kept it as three lines, but it's personal preference – Yorkshireman Apr 09 '20 at 11:56
  • I would recommend adding an `initialValue` incase the array is empty. So, `array.reduce((a, b) => (a.MeasureDate > b.MeasureDate ? a : b), {});`. In this case its `{}`, it could be `null` or anything else you want. – Nikhil Nanjappa Sep 09 '22 at 11:01
25

If you want to get the whole Object, not just the date...

If OP's array of Objects was assigned to a this is how you get the Object with the most recent date:

var mostRecentDate = new Date(Math.max.apply(null, a.map( e => {
   return new Date(e.MeasureDate);
})));
var mostRecentObject = a.filter( e => { 
    var d = new Date( e.MeasureDate ); 
    return d.getTime() == mostRecentDate.getTime();
})[0];
  1. a.map gets the dates from the array of objects.
  2. new Date is applied to each date, making Date Objects
  3. Math.max.apply finds the most recent
  4. We have found the most recent Date, now we need the object.
  5. a.filter loops through the original a array.
  6. We need some way to compare dates, so we use .getTime(), which returns the number of milliseconds since 01/01/1970. This will account for time - if it's defined - as well as date.
  7. When the correct date is found, true is returned, and .filter gives us just that object.

Note: This solution is an extension of @archyqwerty's answer above. Their solution gave only the most recent date from an array of objects, this solution gives you the whole Object that the date was a member of.

Travis Heeter
  • 13,002
  • 13
  • 87
  • 129
8

Modification to Anton Harald's answer: The array I have uses ModDate instead of MeasureDate. I am choosing the most recent date. This works.

  getLatestDate(xs) {
   if (xs.length) {
   return xs.reduce((m,v,i) => (v.ModDate > m.ModDate) && i ? v : m).ModDate;
  }
}

m = accumulator, v = current , i = index

Environment: TypeScript, ES6

Larry Popiel
  • 141
  • 1
  • 4
  • This was useful, although for this to work `xs` needs to be an array. In my particular case I had a dictionary with objects as values, so I was able to use it with `Object.values(myDict).reduce(...)` – Felipe Jan 02 '18 at 20:53
  • @Felipe just be aware that this can't be used only in ES7 https://stackoverflow.com/questions/52304422/property-values-does-not-exist-on-type-objectconstructor – Carmine Tambascia Oct 08 '21 at 18:07
5

Answers rated by performance:

  1. data.reduce((a, b) => a.MeasureDate > b.MeasureDate ? a : b).MeasureDate
  2. data.map(e => e.MeasureDate).reduce((a, b) => a > b ? a : b)
  3. new Date(Math.max.apply(null, data.map(e => new Date(e.MeasureDate)))).toISOString()

(assuming x is the array)

Araxeus
  • 51
  • 1
  • 1
3
function getLatestDate(data) {
   // convert to timestamp and sort
   var sorted_ms = data.map(function(item) {
      return new Date(item.MeasureDate).getTime()
   }).sort(); 
   // take latest
   var latest_ms = sorted_ms[sorted_ms.length-1];
   // convert to js date object 
   return new Date(latest_ms);
}

var data = [{MeasureDate: "2014-10-04T16:10:00"},
            {MeasureDate: "2013-10-04T16:10:00"},
            {MeasureDate: "2012-10-04T16:10:00"}];

getLatestDate(data).toString(); // "Sat Oct 04 2014 18:10:00 GMT+0200 (CEST)"

This function returns the latest date as a JavaScript date Object. You can also turn it into an ISO-String (the format of your source data) with the Date-Object method toISOString().

var date_str = "2012-10-04T16:10:00";
(new Date(date_str)).toISOString(); // "2012-10-04T16:10:00.000Z"

As you can see the result of the method includes always zero milliseconds in the end. If you need your original ISO data-string as a result, you may want to go with the following function:

function getLatestDate2(data) {

   var sorted = data.map(function(item) {
      var MeasureDate = item.MeasureDate;
      return {original_str: MeasureDate,
              in_ms: (new Date(MeasureDate)).getTime()}
   }).sort(function(item1, item2) {
      return (item1.in_ms < item2.in_ms)
   }); 

   // take latest
   var latest = sorted[0];

   return latest.original_str;
}

getLatestDate2(data); // "2014-10-04T16:10:00"
Anton Harald
  • 5,772
  • 4
  • 27
  • 61
2

Also further to @TravisHeeter's answer..

Instead of using 'filter' and grabbing the array index of [0], you can use the .find() method instead as follows:

....

const mostRecentObject = a.find( e => { 
    const d = new Date( e.MeasureDate ); 
    return d.getTime() == mostRecentDate.getTime();
});

This also makes the code more performant, as it will stop looking after it has found the result, rather than filter which will iterate over all objects in the array.

Alex
  • 1,208
  • 16
  • 26
2

By this, you can get the whole object - I just clubbed the process which I learned from Travis Heeter's answer:

let latestDate = Math.max(...a.map((ele) => new Date(ele.dof)));
latestDate = new Date(latestDate);
console.log(latestDate);

let latestObject = a.filter((ele) => {
  let date = new Date(ele.dof);
  return date.getTime() === latestDate.getTime();
});
Zach Jensz
  • 3,650
  • 5
  • 15
  • 30
1

Inspired by many of the suggestions and comments in this thread, here is another solution for the problem. It's very fast, since there is no date object convertion.

function getLatestDate(xs) {
   if (xs.length) {
      return xs.reduce((m, i) => (i.MeasureDate > m) && i || m, "")
               .MeasureDate;
   }
 }

Here's a version for Browser's not supporting arrow functions:

function getLatestDateSave(xs) {
   if (xs.length) {
      return xs.reduce(function(m, i) {
         return (i.MeasureDate > m) && i || m;
      }, "").MeasureDate;
   }
 }
Anton Harald
  • 5,772
  • 4
  • 27
  • 61
1

This how I picked lasest/highest date

var maxLastseen= obj.sort((a,b) => new Date(b.lastS).getTime() - new Date(a.lastS).getTime())[0];
R15
  • 13,982
  • 14
  • 97
  • 173
0
        var dates = []; 

        dates.push(new Date("2019/06/25")); 
        dates.push(new Date("2019/06/26")); 
        dates.push(new Date("2019/06/27")); 
        dates.push(new Date("2019/06/28")); 

        function GFG_Fun() { 
            var maximumDate=new Date(Math.max.apply(null, dates)); 
            var minimumDate=new Date(Math.min.apply(null, dates)); 
        }