160

See edit at end for actual problem.

Ok, I have this scenario:

a = [false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]

Then if I do this:

a.sort(function(a,b){return !a && b});

It gives me this:

[false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, true, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]

It's sorta doing a sort... but not quite... :(

How do I sort this array?

EDIT:

If you are wondering why I did not use just a.sort() is because my actual array is of objects, not a plain array like the one I posted. The real one has elements that look like [{xx:true},{xx:false},...]

Michael Geary
  • 28,450
  • 9
  • 65
  • 75
PCoelho
  • 7,850
  • 11
  • 31
  • 36

14 Answers14

333

a = [false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false];
    
    
    a.sort(function(x, y) {
        // true values first
        return (x === y)? 0 : x? -1 : 1;
        // false values first
        // return (x === y)? 0 : x? 1 : -1;
    });
    
    console.log(a);

You must return 0 when a and b both have the same value, -1 if a is true and 1 otherwise.

Ashish Ranjan
  • 5,523
  • 2
  • 18
  • 39
c.P.u1
  • 16,664
  • 6
  • 46
  • 41
  • 64
    Even shorter: a.sort(function(x, y) { return y - x }); – 000 Jun 30 '13 at 05:33
  • the array that I am sorting is not as simple as the one I posted. Check my edit – PCoelho Jun 30 '13 at 05:38
  • 2
    @PCoelho, consider using Joe's solution. It works and is more concise. – c.P.u1 Jun 30 '13 at 05:41
  • 4
    @JoeFrambach Can you explain how that works or point me to a thread that explains the syntax for something like that? – Adam Moisa Feb 03 '17 at 21:05
  • Thanks for this, now if you were wanting to now sort the list of `true` values a second time, would it be a simple case of first sorting them alphabetically, then the true false sort? – Marais Rossouw Jun 22 '17 at 22:10
  • 14
    @AdamMoisa Joe's solution works because of something called Type Coercion. Basically, the JS engine will notice you're trying to do a math operation (subtraction) on `boolean` values, so it'll convert `false` into `0` and `true` into `1`. – Silvestre Herrera Jul 09 '18 at 23:58
  • @SilvestreHerrera that’s cool and makes sense. Thanks for that! – Adam Moisa Jul 10 '18 at 09:34
  • 1
    @SilvestreHerrera No, there is no Type Coercion in the above example. According to the docs, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort, the compareFunction needs to return -1,0, or 1, which will determine an element's position. The code example above uses nested ternary statements, which, in the case of the uncommented 'truthy values first' example says the following (in english) if x and y are strictly equal return 0, otherwise if x is true return -1 if not return 1; – technicolorenvy Mar 07 '19 at 21:22
  • my solution was list.sort((a, b) => a.booleanProperty && !b.booleanProperty ? 1 : -1); Iäm using typescript so i cannot use - operator. – Janne Harju Sep 23 '19 at 12:00
  • a.sort((a, b) => Boolean(b.property) - Boolean(a.property)); – Gerson Diniz Jul 16 '20 at 15:08
  • 1
    `y - x` does not work in TS with boolean, causes `The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type` – coler-j Nov 11 '20 at 21:25
  • @coler-j in *TypeScript* you can convert booleans to numbers by adding `+` at the beginning. for example `+array.includes((el) => el.x === foo.x)) - ... ` – O-9 Mar 18 '21 at 11:18
  • 1
    In case `y - x` doesn't work for any reason, another method would be to convert `boolean` to `Number` `array.sort((x, y) => y ? 1 : 0 - x ? 1 : 0` – Anwer AR Sep 01 '21 at 18:49
73

To prevent implicit type conversion (which languages like TypeScript don't like), you can use Number() to explicitly convert the boolean to a number:

a = [false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false];
a.sort(function(x, y) {
   return Number(x) - Number(y);
});
console.log(a);

Or using arrow functions:

a = [false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false];
a.sort((x, y) => Number(x) - Number(y));
console.log(a);
sroes
  • 14,663
  • 1
  • 53
  • 72
50

a simpler way:

a = [{xx:true},{xx:false},{xx:true},{xx:false},{xx:true},{xx:false},{xx:true},{xx:false},{xx:true},{xx:false},{xx:true},{xx:false},{xx:true},{xx:false},{xx:true},{xx:false},{xx:true},{xx:false}];

a.sort(function(a,b){return a.xx-b.xx});

console.log(a);

you can call a.reverse() after the sort() if you want it sorted the other way..

EDIT: edited to reflect updated question of sorting an array of objects instead of an array of booleans.

dandavis
  • 16,370
  • 5
  • 40
  • 36
  • 1
    @PranavNegandhi: what's wrong is that i answered the question before an edit re-defined it... – dandavis Jun 30 '13 at 05:42
  • 5
    Great, just if you want it sorted the other way, swap the places of `a` and `b` in the sort function, instead of calling `reverse()`. – Yulian Feb 12 '20 at 17:24
32

An array does not have any equal positions, so why not leave away the equals check, and always return either -1 or 1. This approach works well with TS.

a.sort(x => x ? -1 : 1)

Note: I am a bit concerned how this affects internals of the sort function, but it seems to do the trick.

If you want to reverse sort

a.sort(x => !x ? -1 : 1)
Kristjan Liiva
  • 9,069
  • 3
  • 25
  • 26
29

Simple solution:

[true, false, true, false].sort((a, b) => b - a)

console.log([true, false, true, false].sort((a, b) => b - a));
demo
  • 6,038
  • 19
  • 75
  • 149
4

You can actually just do like the following:

const sortedData = data.sort(
  (a, b) => Number(b) - Number(a),
);

This solution is ok with Typescript has well since the conversion of boolean to number is explicit.

G. Frx
  • 2,350
  • 3
  • 19
  • 31
3

PFB the solution worked for me in Typescript Angular 2 as well,

  let a = [{aa:"1",xx:true},{aa:"10",xx:false},{aa:"2",xx:true},{aa:"11",xx:false},{aa:"3",xx:true},{aa:"12",xx:false},{aa:"4",xx:true},{aa:"13",xx:false},{aa:"5",xx:true},{aa:"14",xx:false},{aa:"6",xx:true},{aa:"15",xx:false},{aa:"7",xx:true},{aa:"16",xx:false},{aa:"8",xx:true},{aa:"17",xx:false},{aa:"9",xx:true},{aa:"18",xx:false}];

    //a.sort(function(a,b){return a.xx-b.xx});
    a.sort(function (x, y) {
        // true values first
        return (x.xx === y.xx) ? 0 : x ? -1 : 1;
        // false values first
        // return (x === y)? 0 : x? 1 : -1;
    });
    return JSON.stringify(a);
dreamdeveloper
  • 1,266
  • 1
  • 15
  • 20
3

I also ran into this issue, here is my part, I hope it helps:

orders.sort((x, y) => {
   if (x === y) return 0;
   if (x) return -1;
   return 1;
});
spedy
  • 2,200
  • 25
  • 26
2

I wanted to see if I could do it without using the ? : operator, just for fun.

Note

This works on all sortable data types (strings, numbers), not just raw booleans. I'm not sure if this is faster than ? : and it's more convoluted. I just get sick of conditionals so it's just personal preference.

  var b = [false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
  .sort((a,b) => Number(a > b) * 2 - 1);

I can turn that into a utility function and give it a meaningful name:

  var sortOrder = {
    asc: (a,b) => Number(a > b) * 2 - 1,
    desc: (a,b) => Number(a < b) * 2 - 1
  }

so then I can:

  var b = [false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
  .sort(sortOrder.asc);
toddmo
  • 20,682
  • 14
  • 97
  • 107
  • 2
    Note that `sort` works with any number not just -1 or 1. Therefore you could use `(a, b) => Number(a > b) - 0.5` to avoid a multiplication. – Xavier Stévenne Jul 02 '20 at 14:18
1

A pretty simple solution for the comparison function is to check if a < b, this gives 0 or 1 when converted to a number. We then want to map 0 to -1 and 1 to 1. To do that you can just multiply by 2 then subtract 1.

data.sort(function (a, b) {
  return (a < b) * 2 - 1
}

or just

data.sort((a, b) => (a < b) * 2 - 1)

Problem sorted!

If any of your values are null they are treated as false (null*2 === 0) and any value that is undefined will become NaN (undefined*2 === NaN) which should make it last in either sort direction.

MattCochrane
  • 2,900
  • 2
  • 25
  • 35
1
a=[true,false,true,false,true];
 
a.sort(function(x, y) {
      a1=x?1:0
      b1=y?1:0
return a1-b1
    });
1

A boolean array with enough entries to represent all transitions i.e. true to true, true to false, false to false, false to true.

var boolarray = [true, false, true, true, false, false, true]
boolarray.sort( (a,b) => !(a ^ b) ? 0 : a ? -1 : 1)

The sort inverts the xor of the inputs. If the inputs are the same then return 0, if they are not then, if the 'a' input is true 'b' must be false so return -1, and vice versa return 1.

'a' and 'b' booleans are sorted when they differ and are ignored when the same.

To use this method with objects just use the object member names for the sort arguments:

var objarray = [{xx:true}, {xx:false}, {xx:true}, {xx:true}, {xx:false}, {xx:false}, {xx:true}]
objarray.sort( (a,b) => !(a.xx ^ b.xx) ? 0 : a.xx ? -1 : 1)
roy
  • 11
  • 2
  • The '^' operator is not allowed for boolean types. Consider using '!==' instead. – Ste Jul 03 '22 at 13:35
0

I got typescript errors on the return (x.xx === y.xx) ? 0 : x ? -1 : 1;

This is my solution when you want to sort on a boolean property

this.mediaList.sort( (a: MediaAutosubscriptionModel, b: MediaAutosubscriptionModel) => {
    let status1: number = a.status === StatusEnum.ACTIVE ? 1 : 0;
    let status2: number = b.status === StatusEnum.ACTIVE ? 1 : 0;
    let comparison: number = 0;
    let direction: number = this.sortDirection === SortDirectionsEnum.ASC ? -1 : 1;
    if (status1 > status2) {
        comparison = direction;
    } else if (status1 < status2) {
        comparison = -1 * direction;
    }
        return comparison;
    });
chriscrossweb
  • 338
  • 1
  • 5
  • 23
0

Without using any ES6 function with time and space optimization -

const data = [false, true, true, true, false, false, false, true];
let lastElementUnchecked;
for(let i=0; i<data.length; i++){
    if(data[i] && lastElementUnchecked !== undefined){
        let temp = data[i];
        data[i] = data[lastElementUnchecked];
        data[lastElementUnchecked] = temp;
        i = lastElementUnchecked;
        lastElementUnchecked = undefined;
    }else{
        if(!data[i] && lastElementUnchecked === undefined){
            lastElementUnchecked = i;
        }
    }
}
console.log(data)
Indrakant
  • 361
  • 3
  • 9