-1

I have an array of objects like this:

[
    {
        created: "2019-08-14T13:24:36Z",
        email: "test1@gmail.com"
    },
    {
        created: "2019-08-15T13:24:36Z",
        email: "test2@gmail.com"
    },
    {
        created: "2019-08-16T13:24:36Z",
        email: "test1@gmail.com"
    },
    {
        created: "2019-08-22T13:24:36Z",
        email: "test4@gmail.com"
    },
    {
        created: "2019-08-22T15:29:66Z",
        email: "test1@gmail.com"
    }
]

The array is sorted by created. I want to filter those records which are on the last day, irrespective of the time on that day. I added the timestamp using moment.js. Something on these lines:

router.get('/GetLastDayRecords', (req, res) => {
    res.json(allRecords.filter(record => record.created.max()));
});
Andreas
  • 21,535
  • 7
  • 47
  • 56
Farhan
  • 2,535
  • 4
  • 32
  • 54
  • 2
    What have you tried so far to solve it on your own? – Andreas Nov 07 '19 at 15:50
  • @Andreas I tried to do [this](https://stackoverflow.com/q/36577205/1050256) but could not figure it out properly. – Farhan Nov 07 '19 at 15:51
  • The array is already sorted. Just grab the last element and iterate over the array to find the elements that should be kept/removed (`Array.prototype.filter()`). – Andreas Nov 07 '19 at 15:52

6 Answers6

2

Split the task: first get the maximum date which you'll find at the end of the sorted array (just getting the "YYYY-MM-DD" part of it is enough) and then launch the filter:

let max = allRecords.length ? allRecords[allRecords.length-1].created.slice(0,10) : "";
res.json(allRecords.filter(({created}) => created >= max));
trincot
  • 317,000
  • 35
  • 244
  • 286
  • This is a great answer! I didn't know you could compare date strings like that, with plain comparison operators (`created >= max`). I had to double check it myself and it works! Brilliant stuff. Thanks. – selmanbey Nov 07 '19 at 16:58
  • 1
    @selmanbey That's no "magic" date string comparison. And it only works because of the yyyy-mm-dd format. – Andreas Nov 07 '19 at 21:07
0

First you need to figure out which day is the last day. If you can assume the records are already sorted, then this is pretty simple:

// Assuming your records are stored in the variable "records"

var lastDay = records[records.length - 1].created;

Now here's where your specific answer may differ based on how you want to handle time zones. Suppose one event happened at 11 PM EST (3 AM GMT) and another event happened at 1 AM EST (5 AM GMT). Are these the same day? In Europe they are, but in America they aren't!

What you need to do is create some cipher from the date+time listed to a "day". This way you can compare two "days" to see if they're the same:

lastDay = new Date(lastDay);
// Setting hours, minutes, and seconds to 0 will give you just the "day" without the time, but by default will use the system timezone
lastDay.setHours(0);
lastDay.setMinutes(0);
lastDay.setSeconds(0);

Once you know which day was the last, it's a simple filter:

// Using a for loop
var results = []
for (var i = 0; i < records.length; i++)
{
    if (records[i].created > lastDay) {
        results.push(records[i]);
    }
}

// Using .filter
var results = records.filter(x => x.created > lastDay);

Alternatively, since we know it's already sorted, we can do it a bit more efficiently by binary searching for the first record on the last day, then grabbing all records after that:

var test = records.length / 2;
var step = records.length / 4;
var found = false;
while (!found) {
    if (records[test].created < lastDay) {
        test += step;
        step /= 2;
    }
    else if (records[test].created > lastDay) {
        if (step == 1) {
            // We found the exact cut-off
            found = true;
        }
        else {
            test -= step;
            step /= 2;
        }
    }
}

var results = records.slice(test);

Because you're only interested in the "last" day, the logic is a bit simpler. If you wanted the "third" day, you would need to check if created was after the start of the third day and before the end of the third day. We can just check if it's after the start of the last day.

stevendesu
  • 15,753
  • 22
  • 105
  • 182
0

This should work:

allRecords.filter( record => {
    let last_date = allRecords[ allRecords.length - 1].created
    return last_date.slice(0, 10) === record.created.slice(0, 10)
})

Basically, you are getting the last element from your array and slicing its created value down to its date. Then you are slicing your current record's created value down to its date and comparing if they are the same.

selmanbey
  • 318
  • 1
  • 8
0

I wrote a solution using reduce and filter:

const lastDay = arr.reduce((acc, el) => {
    const date = el.created.substr(0,10);
    const oldDate = new Date(acc);
    const nextDate = new Date(date);
    if(oldDate.getTime() > nextDate.getTime()) {
        return oldDate;
    } else {
        return nextDate;
    }
}, '1900-01-01');

const lastDayArr = arr.filter(el => {
    const date = el.created.substr(0,10);
    const oldDate = new Date(lastDay);
    const nextDate = new Date(date);
    return (oldDate.getTime() === nextDate.getTime());
});

First, you find the most recent date, reducing the original array by comparing which date is the most recent, for this you drop the part of the created string that specifies the hours/minutes/seconds. You can use a very distant in time date as initial value, or you can set it to null and add another validation in your callback function.

As a second step, you use filter, using the same technique of dropping the hours/minutes/seconds of the created string.

The end result is an array of the elements with the most recent date in your original array.

If you can assume the array is sorted, you can skip the reduce method and just do:

const lastDay = arr[arr.length - 1].created.substr(0,10);
victor.ja
  • 811
  • 1
  • 7
  • 27
  • It's not the most succinct solution, but I think it's the easiest to read and reason about for other programmers – victor.ja Nov 07 '19 at 16:23
0

I would create a function to turn your created properties into data be easily compared.

I would also avoid trying to do the entire filter operation in one or two lines as it will difficult to read by other developers.

const dateToInt = date => parseInt( date.split('T').shift().replace(/-/g, '') );

The above will:

  • Split your created property into an array of date and time.
  • Select the first element, which happens to be the date.
  • Remove the dashes in the date.
  • Coerce the value into a number.

With this you can find the maximum value and filter based on that value.


const nums = foo.map( ({ created }) => dateToInt(created) )

First get a list of numbers from the dataset.

const max = Math.max( ...nums )

Get the biggest number in the list.

const lastDays = foo.filter( ({ created }) => dateToInt(created) === max )

With all that setup, getting the max date is very easy and readable.


Of course, since the list is already sorted. You could have just done this as well.

const last = foo[foo.length -1].created;

const lastDays = foo.filter( ({ created }) => created === last )
Cameron Sanders
  • 189
  • 1
  • 6
0

Assuming that the array is already ASC ordered:

const onLastDay = values.filter( v => {
  const last = moment(values[ values.length - 1 ].created)
  const differenceInDays = last.diff(moment(v.created), 'days')
  return differenceInDays < 1
})

console.log(onLastDay)

NOTE: If you try with the reported array you get an error due the fact that the last date is not valid! There are 66 seconds!

Elia
  • 1,417
  • 1
  • 18
  • 28