Javascript doesn't sort correctly an array including two arrays' values

I have two arrays in Javascript coming from Php. I merge these arrays into one array.

All of arrays' elements has created_at value (Laravel). I want to sort these values by created_at

Problem: Regardless of their date. First array's elements never take place in behind of those of second array

Example: B has latest date. But even so, C (comes from second array) takes place after B.

The problem is although I merged these two arrays into one array. Javascript still thinks "there are two arrays. I should sort first array's elements then those of second array."

What do I do is :

history.push(...response.data[0]); // first array's values
history.push(...response.data[1]); // second array's values

history.sort((a, b) => {
            return a.created_at - b.created_at;
          });

so, history like

[

// Comes from first array

 {
  name: 'A',
  created_at: '08/09/2021'
 },

// Comes from first array

{
  name: 'B',
  created_at: '15/09/2021'
 },


// This third element comes from second array.

{
  name: 'C',
  created_at: '08/09/2021'
 }
]

I expect this result:

new sorted history:


 {
  name: 'A',
  created_at: '08/09/2021'
 },

{
  name: 'C',
  created_at: '08/09/2021'
 },

{
  name: 'B',
  created_at: '15/09/2021'
 }



But Javascript initially sorts first array's element. After, sort second array's element then what comes out is:


 {
  name: 'A',
  created_at: '08/09/2021'
 },

{
  name: 'B',
  created_at: '15/09/2021'
 },
{
  name: 'C',
  created_at: '08/09/2021'
 },

Upvotes: 3

Views: 187

Answers (3)

Yosvel Quintero
Yosvel Quintero

Reputation: 19070

To sort by date values you can use Date.prototype.getTime():

const arr1 = [{name: 'A', created_at: '08/09/2021'}]
const arr2 = [{name: 'B', created_at: '15/09/2021'}, {name: 'C', created_at: '08/09/2021'}]

const toNumber= d => +d.replace(/\//g, '')
const result =  [...arr1, ...arr2].sort((a, b) => toNumber(a.created_at) - toNumber(b.created_at))

console.log(result)

Upvotes: 0

Chris Welton
Chris Welton

Reputation: 106

You need to send a custom sort function to Array.sort()

const array = 
[
    { name: 'A', created_at: '08/09/2021' },
    { name: 'B', created_at: '15/09/2021' },
    { name: 'C', created_at: '08/09/2021' }
];

const ymd = (dmy) => { let a = dmy.split('/'); return a[2] + '/' + a[1] + '/' + a[0] }

const sorted = array.sort((elem1, elem2) => ymd(elem1.created_at) > ymd(elem2.created_at) ? 1 : -1);
console.log(sorted);

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

Edit:

If you need the sort to be stable (meaning it does not ever arbitrarily swap objects with equivalent comparison values) or by date first and alpha second, then you will need a more complex sort function.

With a little more time and a localeCompare() suggestion form Felix Kling I wrote an improved sort that is stable and will sort by date first and name second.

const array =
    [
        { name: 'A', created_at: '08/09/2021' },
        { name: 'Z', created_at: '15/09/2021' },
        { name: 'D', created_at: '15/09/2021' },
        { name: 'B', created_at: '15/09/2021' },
        { name: 'C', created_at: '08/09/2021', stable: '1'},
        { name: 'S', created_at: '08/09/2020' },
        { name: 'C', created_at: '08/09/2021', stable: '2' },
        { name: 'C', created_at: '08/06/2021' }
    ];

const ymd = (dmy) => { let a = dmy.split('/'); return a[2] + '/' + a[1] + '/' + a[0] }

const sorted = array.sort((elem1, elem2) =>
    (ymd(elem1.created_at) === ymd(elem2.created_at)) ?
        elem1.name.localeCompare(elem2.name) :
        ymd(elem1.created_at).localeCompare(ymd(elem2.created_at)));

console.log(sorted);

Outputs:

[
  { name: 'S', created_at: '08/09/2020' },
  { name: 'C', created_at: '08/06/2021' },
  { name: 'A', created_at: '08/09/2021' },
  { name: 'C', created_at: '08/09/2021', stable: '1' },
  { name: 'C', created_at: '08/09/2021', stable: '2' },
  { name: 'B', created_at: '15/09/2021' },
  { name: 'D', created_at: '15/09/2021' },
  { name: 'Z', created_at: '15/09/2021' }
]

Upvotes: 1

Hassan Imam
Hassan Imam

Reputation: 22524

You can array#concat both your array and use Schwartzian transform to sort your array by converting the created_at to YYYY-MM-DD format which could be sorted lexicographically.

const arr1 = [{name: 'A', created_at: '08/09/2021'}],
      arr2 = [{name: 'B', created_at: '15/09/2021'}, {name: 'C', created_at: '08/09/2021'}],
      result = arr1.concat(arr2)
                   .map(o => [o.created_at.replace(/(..)\/(..)\/(....)/, '$3-$2-$1'), o])
                   .sort((a,b) => a[0].localeCompare(b[0]))
                   .map(([,o]) => o);
console.log(result)

Upvotes: 4

Related Questions