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.

3
  • Why do you have the keys in your array? Commented Apr 16, 2020 at 15:28
  • 2
    Is the property date or _id? Commented Apr 16, 2020 at 15:28
  • date, sry I just changed. Commented Apr 16, 2020 at 15:29

3 Answers 3

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;
}
Sign up to request clarification or add additional context in comments.

1 Comment

That was great, thank you so much for the explanation. :-)
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.

3 Comments

I'd suggest repl = "$3-$2-$1"; instead, since that's an officially supported format.
Is specifically ISO 8601
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). ;-)
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);

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.