I have an array of objects like so for example:
[ {
id: '-1'
},
{
id: '10'
},
{
id: '1234'
},
{
id: '1235'
},
{
id: '-1'
} ]
I would like to sort this array so that it is ordered by the ids ascending (smallest to largest), however I would like objects that have the id of '-1' to be sent to the back of the array. So I tried this:
const arr = [ { id: '-1' }, { id: '10'}, { id: '1234' }, { id: '1235' }, { id: '-1' } ]
arr.sort((a, b) => {
if (parseInt(a.id) === -1 || parseInt(b.id) === -1) return 1;
if (parseInt(a.id) - parseInt(b.id)) return -1;
});
console.log(arr);
As you can see from the snippet above it sorts them successfully ascending however it doesn't successfully move ids with '-1' to the back of the array.
Why does this happen? What am I doing wrong?
First, let's take a look at what .sort
expects you to feed it.
Given paramaters (a, b)
-- we should return
< 0
if a < b> 0
if a > bLet's take a look at your first line
if (parseInt(a.id) === -1 || parseInt(b.id) === -1) return 1;
-1
means "a is smaller". But that might not be true. It might be that a
(not b
) is -1, and is bigger, yet we're always telling JavaScript to send a
to the back when that's what we should be doing to b
.
Instead, we want to return -1
when b
is -1, but +1
when a
is -1. It doesn't really matter what we return if they're both -1.
In JavaScript, any number except for 0
is truthful. Let's take a look at your next line.
if (parseInt(a.id) - parseInt(b.id)) return -1;
If a - b
is 0, we don't return anything in this callback. If it isn't 0, we always say that a < b.
Notice that never do we say that b < a -- so if such an event were to occur, we couldn't handle it and would sort it incorrectly.
const aID = parseInt(a.id)
const bID = parseInt(b.id)
a
or b
-1?if(aID === -1) return +1;
if(bID === -1) return -1;
a
or b
If you assume that a
and b
are not -1
, then we can simply subtract a - b
. Why? If a
is bigger than b
, then the subtraction will create a positive number to say b < a. If a
is less than b
, then the subtraction will create a negative number, to say a < b.
return aID - bID
const arr = [ { id: '-1' }, { id: '10'}, { id: '1234' }, { id: '1235' }, { id: '-1' } ]
arr.sort((a, b) => {
const [aID, bID] = [parseInt(a.id), parseInt(b.id)]
if(aID === -1) return +1
if(bID === -1) return -1
return aID - bID
});
console.log(arr);
It might be helpful to make things shorter. Another answer, by @Nina Scholz, helpfully showed a much shorter version. I thought it might be useful to explain why this works.
return (a.id === '-1') - (b.id === '-1') || a.id - b.id
x || y
x || y
means:
x
is truthful, return x.x
isn't truthful, return y.(aID === -1)
This means true
if aID is -1, and false
otherwise
(aID === -1) - (bID === -1)
How can you subtract true
and false
? true
will be interpreted as 1
, and false
as 0
.
This value will be 1 - 0
, or +1
This value will be 0 - 1
, or -1
Remember, it doesn't matter what we return if the two values are the same
0 - 0
. This is 0
. This is not a truthful value. So we go into the ||
bit. Which, in that answer, has the second bit be aID - bID
, as described above. It's very clever and very short, though might not be as readable.