Home > Net >  How to sort array of objects by date but with most current date first?
How to sort array of objects by date but with most current date first?

Time:05-11

I've got the array of objects:

const data = [{
  "id": "1",
  "effectiveDate": "2023-01-21"
}, {
  "id": "2",
  "effectiveDate": "2023-02-22"
}, {
  "id": "3",
  "effectiveDate": "2022-05-04"
}, {
  "id": "4",
  "effectiveDate": "2022-05-05"
}, {
  "id": "5",
  "effectiveDate": "2021-01-21"
}, {
  "id": "6",
  "effectiveDate": "2021-02-22"
}];

What I'm after is the way to sort it in the way that the object with the most current date is on index 0 and the rest of the objects are ordered ascending from the oldest to future dates, like this:

[{
  "id": "4",
  "effectiveDate": "2022-05-05"
}, {
  "id": "5",
  "effectiveDate": "2021-01-21"
}, {
  "id": "6",
  "effectiveDate": "2021-02-22"
}, {
  "id": "3",
  "effectiveDate": "2022-05-04"
}, {
  "id": "1",
  "effectiveDate": "2023-01-21"
}, {
  "id": "2",
  "effectiveDate": "2023-02-22"
}]

The way I'm sorting it is:

const orderedDates = data.sort((a, b) => {
  const dateCompareResult = new Date(a.effectiveDate) - new Date(b.effectiveDate);
  return dateCompareResult;
});

which obviously gives me dates sorted ascending from the past dates to the future dates with the most current date somewhere in between. How can I move the most current date object to index 0?

CodePudding user response:

function getItemsInAscendingDateOrderAndClosestToNowFirst(arr) {
  const time = Date.now();

  const [closest, ...rest] = Array
    // create shallow copy in order to
    // not mutate the original reference.
    .from(arr)
    // sort items by date closest to now.
    .sort((a, b) => {

      const aTime = new Date(a.effectiveDate).getTime();
      const bTime = new Date(b.effectiveDate).getTime();

      const aDelta = Math.abs(time - aTime);
      const bDelta = Math.abs(time - bTime);

      return (aDelta - bDelta);
    });

  return [
    closest,
    ...rest
      // sort all other items in ascending date order.
      .sort((a, b) =>
        new Date(a.effectiveDate).getTime()
        - new Date(b.effectiveDate).getTime()
      ),
  ];
}

const data = [{
  "id": "1",
  "effectiveDate": "2023-01-21"
}, {
  "id": "2",
  "effectiveDate": "2023-02-22"
}, {
  "id": "3",
  "effectiveDate": "2022-05-04"
}, {
  "id": "4",
  "effectiveDate": "2022-05-05"
}, {
  "id": "5",
  "effectiveDate": "2021-01-21"
}, {
  "id": "6",
  "effectiveDate": "2021-02-22"
}];
const sortedData =
  getItemsInAscendingDateOrderAndClosestToNowFirst(data);

console.log({ sortedData });
.as-console-wrapper { min-height: 100%!important; top: 0; }

CodePudding user response:

I think the simplest way is to sort from oldest to newest, then move the element that's closest to today to the start, e.g.

let data = [{"id": "1","effectiveDate": "2023-01-21"},
            {"id": "2","effectiveDate": "2023-02-22"},
            {"id": "3","effectiveDate": "2022-05-04"},
            {"id": "4","effectiveDate": "2022-05-05"},
            {"id": "5","effectiveDate": "2021-01-21"},
            {"id": "6","effectiveDate": "2021-02-22"}];

// Sort from oldest to youngest
data.sort((a, b) => b.effectiveDate.localeCompare(a.effectiveDate));

// Get today as milliseconds
let today = new Date().toLocaleDateString('en-CA');
let todayMS = Date.parse(today);

// Get index of closest element to today
let closestIndex = data.reduce((acc, obj, index) => {
  let diff = Math.abs(Date.parse(obj.effectiveDate) - todayMS);
  return acc.diff > diff? {diff, index} : acc;
}, {diff: Infinity, index:null}).index;

// Move closest element to start of array
data.unshift(data.splice(closestIndex, 1));

console.log(data);

One of the benefits of ISO 8601 formatted timestamps is that lexical comparisons give correct results. If you don't want to use string comparison then the comparison function can be:

data.sort((a, b) => Date.parse(b) - Date.parse(a))

but only if the timestamps are in one of the 3 formats supported by ECMA-262 (and fortunately, YYYY-MM-DD is one such format). :-)

  • Related