1

I understand that to sort integers, I need to pass a compare function to sort() like so:

function(a, b){return a-b}

But what I'm wondering is how this actually works. Meaning, if the result of a-b is negative, then b is sorted ahead of a, and if a-b is positive, then a is sorted ahead of b? Is that right? What happens if a-b = 0?

Brispo
  • 45
  • 1
  • 7
  • 2
    Then the numbers are the same, and it doesn't matter which goes first. It's [pretty well documented](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Description). – Paul Roub Sep 17 '15 at 16:57

2 Answers2

2

That's a common pattern for sorting APIs. If the returned value is 0, that means that the items are (for the purposes of ordering) equal.

In practice, the behavior of a sort function with equal keys is of some importance. A sort that leaves entries in the order they were in (relative to each other) when the keys are equal is called a stable sort. In JavaScript, the .sort() API is explicitly specified as not necessarily being stable. That means that the sort function might leave you with an array wherein two elements that compared as equal are swapped when the sort is complete.

That behavior has an effect on how you use the .sort() function. Let's say you have a list of objects:

var list = [{
  firstName: "Louis",
  lastName: "Adams",
  // ...
}, {
  // ...
}];

So, objects with a first name field and a last name field. If the JavaScript .sort() API were required to be stable, then you could sort the list in a way that people would expect it to be sorted by doing this:

function ocompare(key, o1, o2) {
  return o1[key] < o2[key] ? -1 :
         o1[key] > o2[key] ? 1 :
         0;
}
list.sort(function(o1, o2) {
  return ocompare("firstName", o1, o2);
});
list.sort(function(o1, o2) {
  return ocompare("lastName", o1, o2);
});

That is, you could sort on the first name, and then sort on the last name, and you'd have a list sorted by last names, and you'd be sure that "Adams, John" would be in the list before "Adams, Zach". (Note that the first thing we sort on is the less important key.)

However, the JavaScript .sort() API is not necessarily stable, so you can't rely on that. Instead, you have to do all the ordering comparisons you want in a single pass through .sort():

function ocompare(o1, o2) {
  var keys = [];
  for (var i = 2; i < arguments.length; keys[i] = arguments[i], ++i);

  for (i = 0; i < keys.length; ++i) {
    if (o1[keys[i]] < o2[keys[i]])
      return -1;
    if (o1[keys[i]] > o2[keys[i]])
      return 1;
  }
  return 0;
}
list.sort(function(o1, o2) {
  return ocompare(o1, o2, "lastName", "firstName");
});

Note that this (while a bit more involved) might be faster than doing the two-pass approach that relies on a stable sort, because no comparison is necessary when the last name fields differ.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • I tend to copy stuff out of the `arguments` object as a habit; that's not really necessary here but I think it makes it neater. In ES2015 it'd be possible to use the `...` notation. – Pointy Sep 17 '15 at 17:08
1

Meaning, if the result of a-b is negative, then b is sorted ahead of a, and if a-b is positive, then a is sorted ahead of b? Is that right?

It's the other way round. If the return value is negative, a is put before b.

What happens if a-b = 0?

It means the values are equal and their relative order does not matter.

From the MDN documentation:

If compareFunction is supplied, the array elements are sorted according to the return value of the compare function. If a and b are two elements being compared, then:

  • If compareFunction(a, b) is less than 0, sort a to a lower index than b, i.e. a comes first.

  • If compareFunction(a, b) returns 0, leave a and b unchanged with respect to each other, but sorted with respect to all different elements. Note: the ECMAscript standard does not guarantee this behaviour, and thus not all browsers (e.g. Mozilla versions dating back to at least 2003) respect this.

  • If compareFunction(a, b) is greater than 0, sort b to a lower index than a.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143