1
[
0: {date: "02-04-2020", count: 1}
1: {date: "16-04-2020", count: 2}
2: {date: "10-04-2020", count: 1}
3: {date: "15-04-2020", count: 4}
4: {date: "04-04-2020", count: 4}
]

From the above array of objects I need to sort them according to date and get the output as follows

    [
    0: {date: "02-04-2020", count: 1}
    1: {date: "04-04-2020", count: 4}
    2: {date: "10-04-2020", count: 1}
    3: {date: "15-04-2020", count: 4}
    4: {date: "16-04-2020", count: 2}
    ]

I tried using the following method

const sorted_array = data
          .slice()
          .sort(
            (a, b) => new Date(a.date).valueOf() - new Date(b.date).valueOf()
          );

I used .slice before sort since I get the error Cannot assign to read only property '0' of object '[object Array] so I referred Error while sorting array of objects Cannot assign to read only property '2' of object '[object Array]' and added slice.

But the sorting is not done correctly, I need some help in fixing this. Thank you.

PremKumar
  • 1,282
  • 4
  • 25
  • 43

3 Answers3

2

You might be encountering an issue with new Date(...) not handling strings of this format since it's not a standard format. new Date("02-04-2020") interprets it as February 4, 2020, and new Date("16-04-2020") produces an error. It's seeming to use American date formats here, where we backwardsly use month before day.

If your dates were in the format yyyy-mm-dd, then new Date(...) would work properly, so if you're able to change that, that'll be the smoothest route. If you cannot change the format, you'll want to consider changing the sorting thusly:

const sorted_array = data
  .map(addSortableColumn)
  .sort(bySortColumn)
  .map(removeSortableColumn);

function addSortableColumn(item) {
  const [day, month, year] = item.date.split('-');
  return { ...item, _sortBy: year + month + day };
}

function bySortColumn(a, b) {
  return a._sortBy.localeCompare(b._sortBy);
}

function removeSortableColumn(item) {
  const { _sortBy, ...otherProps } = item;
  return otherProps;
}

(if you don't care about removing the sort column, you can skip that last step)

Update

If you need to preserve object prototypes, it was correctly pointed out that spread syntax destroys this. Another approach that I actually like even better is to store the sort columns in a separate map. The reason why it's even being stored is to avoid the sort keys needing to be computed multiple times.

const sortKeys = new Map();

const sorted_array = data.slice().sort(byDDMMYYYY);

function byDDMMYYYY(a, b) {
  return getSortKey(a).localeCompare(getSortKey(b));
}

function getSortKey(item) {
  let key = sortKeys.get(item);
  if (!key) {
    const [day, month, year] = item.date.split('-');
    key = year + month + day;
    sortKeys.set(item, key);
  }
  return key;
}
Jacob
  • 77,566
  • 24
  • 149
  • 228
2

Refer to @Jacob's answer for an explanation of your mistake. Here's an alternative solution using regex to change your date to the correct format:

const pattern = /(\d{2})-(\d{2})-(\d{4})/;
const repl = "$3-$2-$1";
const sorted_array = data
      .slice()
      .sort(
          (a, b) => new Date(a.date.replace(pattern, repl)) - 
                    new Date(b.date.replace(pattern, repl))
      );

Also note that you don't need to compare valueOf() from the Dates, arithmetic has been overloaded for Date already to allow native comparison of the objects.

Edit: changed repl to ISO 8601-compatible format of YYYY-MM-DD according to the specification.

  • 1
    I'd suggest `repl = "$3-$2-$1";` instead, since that's an _officially_ supported format. – Jacob Apr 16 '20 at 15:49
  • Is specifically ISO 8601 – Jacob Apr 16 '20 at 15:56
  • 1
    While `new Date(a.date.replace(pattern, repl))` works in this case, it's an incorrect parse as it treats the date as UTC. Creating a Date isn't necessary, comparing "$3-$2-$1" as strings is sufficient (and possibly more efficient). ;-) – RobG Apr 16 '20 at 20:23
1

Your date format is not correct. DD-MM-YYYY is not a valid date format.

new Date("15-04-2020")

will return Invalid Date

You need to convert your date to ISO date format YYYY-MM-DD according to the specification.

const data = [ 
  {_id: "02-04-2020", count: 1},
  {_id: "16-04-2020", count: 2},
  {_id: "10-04-2020", count: 1},
  {_id: "15-04-2020", count: 4},
  {_id: "04-04-2020", count: 4},
];

console.log("Original Data: ", data);

// Something like that.
const dataSorted = data.slice().sort((a, b) => {
  const [dayA, monthA, yearA] = a._id.split("-");
  const [dayB, monthB, yearB] = b._id.split("-");
  return new Date(`${yearA}-${monthA}-${dayA}`) 
    - new Date(`${yearB}-${monthB}-${dayB}`)
});

console.log("Data sorted by date: ", dataSorted);
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
Kevin
  • 482
  • 1
  • 4
  • 10