2

I have the array:

var arr = [
    {id: "d", sequence: 3},
    {id: "c", sequence: 4},
    {id: "b", sequence: 2},
    {id: "a", sequence: 1},
    {id: "e"}
];

and want to sort it with follow logic:

  • element without sequence should be first
  • other should be sorted by descending sequence.

My solution is:

arr.sort(function(a, b) {
  if (!a.sequence || !b.sequence) return 1;
  return b.sequence - a.sequence;
});

I've expected following order:

[{id: "e"}, {id: "c", sequence: 4}, {id: "d", sequence: 3}, ...]

but in safari receive:

[
  {id: "c", sequence: 4}, 
  {id: "d", sequence: 3},
  {id: "b", sequence: 2},
  {id: "a", sequence: 1},
  {id: "e"}
]

Why in Safari {id: "e"} is the last element, when in Chrome and Firefox it is first?

Thanks for advance!

kukkuz
  • 41,512
  • 6
  • 59
  • 95
Nata Khitsan
  • 288
  • 3
  • 12
  • 1
    Always returning `1` regardless which of the two (or both) have no `.sequence` property cannot work, it's not a [valid comparison function](https://stackoverflow.com/a/24080786/1048572). That it worked in Chrome and Firefox is more by luck. – Bergi Aug 14 '17 at 13:54
  • 2
    A shorter code than Pointy's would be `function(a, b) { return (b.sequence || Infinity) - (a.sequence || Infinity); }`. – Bergi Aug 14 '17 at 13:58

2 Answers2

7

Your sort comparator does not do what you say you want it to do. If you want the lack of a "sequence" property to mean an element should be before elements with such a property, your return value has to reflect that:

arr.sort(function(a, b) {
  if (("sequence" in a) && !("sequence" in b))
    return 1;
  if (("sequence" in b) && !("sequence" in a))
    return -1;
  if (!("sequence" in a) && !("sequence" in b))
    return 0;
  return b.sequence - a.sequence;
});

(This could probably be done with a little less code; it's explicit for clarity.) It's important that a sort comparator function always returns consistent results for every pair of elements (in either order).

Your existing code behaves differently between browsers because sort implementations vary between runtime systems.

Pointy
  • 405,095
  • 59
  • 585
  • 614
1

You can use #sort like this:

  1. The first part of the return expression checks if sequence exists,
  2. while the second part order the arr in descending order.

See demo below:

var arr=[{id:"d",sequence:3},{id:"c",sequence:4},{id:"b",sequence:2},{id:"a",sequence:1},{id:"e"}];

var result = arr.sort(function(a, b) {
  return +('sequence' in a) - +('sequence' in b) || b.sequence - a.sequence
});

console.log(result);
.as-console-wrapper{top:0;max-height:100%!important;}
kukkuz
  • 41,512
  • 6
  • 59
  • 95