140

How does the following code sort this array to be in numerical order?

var array=[25, 8, 7, 41]

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

I know that if the result of the computation is...

Less than 0: "a" is sorted to be a lower index than "b".
Zero: "a" and "b" are considered equal, and no sorting is performed.
Greater than 0: "b" is sorted to be a lower index than "a".

Is the array sort callback function called many times during the course of the sort?

If so, I'd like to know which two numbers are passed into the function each time. I assumed it first took "25"(a) and "8"(b), followed by "7"(a) and "41"(b), so:

25(a) - 8(b) = 17 (greater than zero, so sort "b" to be a lower index than "a"): 8, 25

7(a) - 41(b) = -34 (less than zero, so sort "a" to be a lower index than "b": 7, 41

How are the two sets of numbers then sorted in relation to one another?

Please help a struggling newbie!

0

8 Answers 8

73

Is the array sort callback function called many times during the course of the sort?

Yes

If so, I'd like to know which two numbers are passed into the function each time

You could find out your self with:

array.sort((a,b) => {
  console.log(`comparing ${a},${b}`);
  return a > b ? 1
               : a === b ? 0 
                         : -1;
});

EDIT

This is the output I've got:

25,8
25,7
8,7
25,41
6
  • 8
    rather do a console.log to firebug or html DIV element's .innerHTML += "comparing " + a + ", " + b + "\n";
    – Joshua
    Commented Sep 29, 2009 at 20:32
  • 3
    Remember that this is a wiki-like site and we can edit others answers to make them better :) Commented Jul 9, 2011 at 23:29
  • 10
    Just a note for new ES6 syntax: array.sort((a,b) => a - b); is valid syntax Commented Nov 30, 2015 at 20:12
  • 1
    if sort function return -ve then it swap and +ve then it don't swap ??
    – Mahi
    Commented Dec 6, 2016 at 12:27
  • 2
    @ShekharReddy you can still use operators to compare. I've updated the answer.
    – OscarRyz
    Commented Mar 7, 2018 at 16:38
57

The JavaScript interpreter has some kind of sort algorithm implementation built into it. It calls the comparison function some number of times during the sorting operation. The number of times the comparison function gets called depends on the particular algorithm, the data to be sorted, and the order it is in prior to the sort.

Some sort algorithms perform poorly on already-sorted lists because it causes them to make far more comparisons than in the typical case. Others cope well with pre-sorted lists, but have other cases where they can be "tricked" into performing poorly.

There are many sorting algorithms in common use because no single algorithm is perfect for all purposes. The two most often used for generic sorting are Quicksort and merge sort. Quicksort is often the faster of the two, but merge sort has some nice properties that can make it a better overall choice. Merge sort is stable, while Quicksort is not. Both algorithms are parallelizable, but the way merge sort works makes a parallel implementation more efficient, all else being equal.

Your particular JavaScript interpreter may use one of those algorithms or something else entirely. The ECMAScript standard does not specify which algorithm a conforming implementation must use. It even explicitly disavows the need for stability.

3
  • 1
    FWIW, basic Quicksort is one of the algorithms that can be "tricked" into performing poorly. In the simple form, it has O(N^2) performance for lists that are either already sorted or exactly reversed. Most library Quicksort algorithms have a number of non-obvious optimizations which help avoid these common worst case scenarios.
    – Adisak
    Commented Sep 29, 2009 at 20:42
  • 3
    JavaScriptCore actually uses an AVL tree for sorting as it is necessary to behave deterministically in the face of comparator functions that modify the array being sorted.
    – olliej
    Commented Sep 30, 2009 at 2:14
  • 3
    ECMAScript standard now requires stability.
    – ggorlen
    Commented Oct 6, 2020 at 23:06
14

Deeply Knowledge

If the result is negative a is sorted before b.

If the result is positive b is sorted before a.

If the result is 0 no changes are done with the sort order of the two values.

NOTE:

This code is the view inside of the sort method step by step.

OUTPUT:

let arr = [90, 1, 20, 14, 3, 55];
var sortRes = [];
var copy = arr.slice();		//create duplicate array
var inc = 0;	//inc meant increment
copy.sort((a, b) => {
	sortRes[inc] = [ a, b, a-b ];
	inc += 1;
	return a - b;
});
var p = 0;
for (var i = 0; i < inc; i++) {
	copy = arr.slice();
	copy.sort((a, b) => {
		p += 1;
		if (p <= i ) {
			return a - b;
		}
		else{
			return false;
		}
	});
	p = 0;
	console.log(copy +' \t a: '+ sortRes[i][0] +' \tb: '+ sortRes[i][1] +'\tTotal: '+ sortRes[i][2]);
}

1
  • The logic I derived by running the code snippet is this: "a" always gets the value in the array that is after the value of "b". In other words "a" always points to a value with a higher index in the current array than the index of the value "b" points at. And because of that when "a"'s value is bigger and we subtract from "a", the array gets sorted in ascending order.
    – luzede
    Commented Jul 15 at 20:38
13

Pairs of values are compared, one pair at a time. The pairs that are compared are an implementation detail--don't assume they will be the same on every browser. The callback can be anything (so you can sort strings or Roman numerals or anything else where you can come up with a function that returns 1,0,-1).

One thing to keep in mind with JavaScript's sort is that it is not guaranteed to be stable.

0
10

To help clarify the behavior of Array#sort and its comparator, consider this naive insertion sort taught in beginning programming courses:

const sort = arr => {
  for (let i = 1; i < arr.length; i++) {
    for (let j = i; j && arr[j-1] > arr[j]; j--) {
      [arr[j], arr[j-1]] = [arr[j-1], arr[j]];
    }
  }
};

const array = [3, 0, 4, 5, 2, 2, 2, 1, 2, 2, 0];
sort(array);
console.log("" + array);

Ignoring the choice of insertion sort as the algorithm, focus on the hardcoded comparator: arr[j-1] > arr[j]. This has two problems relevant to the discussion:

  1. The > operator is invoked on pairs of array elements but many things you might want to sort such as objects don't respond to > in a reasonable way (the same would be true if we used -).
  2. Even if you are working with numbers, oftentimes you want some other arrangement than the ascending sort that's been baked-in here.

We can fix these problems by adding a comparefn argument which you're familiar with:

const sort = (arr, comparefn) => {
  for (let i = 1; i < arr.length; i++) {
    for (let j = i; j && comparefn(arr[j-1], arr[j]) > 0; j--) {
      [arr[j], arr[j-1]] = [arr[j-1], arr[j]];
    }
  }
};

const array = [3, 0, 4, 5, 2, 2, 2, 1, 2, 2, 0];
sort(array, (a, b) => a - b);
console.log("" + array);

sort(array, (a, b) => b - a);
console.log("" + array);

const objArray = [{id: "c"}, {id: "a"}, {id: "d"}, {id: "b"}];
sort(objArray, (a, b) => a.id.localeCompare(b.id));
console.log(JSON.stringify(objArray, null, 2));

Now the naive sort routine is generalized. You can see exactly when this callback is invoked, answering your first set of concerns:

Is the array sort callback function called many times during the course of the sort? If so, I'd like to know which two numbers are passed into the function each time

Running the code below shows that, yes, the function is called many times and you can use console.log to see which numbers were passed in:

const sort = (arr, comparefn) => {
  for (let i = 1; i < arr.length; i++) {
    for (let j = i; j && comparefn(arr[j-1], arr[j]) > 0; j--) {
      [arr[j], arr[j-1]] = [arr[j-1], arr[j]];
    }
  }
};

console.log("on our version:");
const array = [3, 0, 4, 5];
sort(array, (a, b) => console.log(a, b) || (a - b));
console.log("" + array);

console.log("on the builtin:");
console.log("" + 
  [3, 0, 4, 5].sort((a, b) => console.log(a, b) || (a - b))
);

You ask:

How are the two sets of numbers then sorted in relation to one another?

To be precise with terminology, a and b aren't sets of numbers--they're objects in the array (in your example, they're numbers).

The truth is, it doesn't matter how they're sorted because it's implementation-dependent. Had I used a different sort algorithm than insertion sort, the comparator would probably be invoked on different pairs of numbers, but at the end of the sort call, the invariant that matters to the JS programmer is that the result array is sorted according to the comparator, assuming the comparator returns values that adhere to the contract you stated (< 0 when a < b, 0 when a === b and > 0 when a > b).

In the same sense that I have the freedom to change my sort's implementation as long as I don't breach my specification, implementations of ECMAScript are free to choose the sort implementation within the confines of the language specification, so Array#sort will likely produce different comparator calls on different engines. One would not write code where the logic relies on some particular sequence of comparisons (nor should the comparator produce side effects in the first place).

For example, the V8 engine (at the time of writing) invokes Timsort when the array is larger than some precomputed number of elements and uses a binary insertion sort for small array chunks. However, it used to use quicksort which is unstable and would likely give a different sequence of arguments and calls to the comparator.

Since different sort implementations use the return value of the comparator function differently, this can lead to surprising behavior when the comparator doesn't adhere to the contract. See this thread for an example.

6

Is the array sort callback function called many times during the course of the sort?

Yes, that's exactly it. The callback is used to compare pairs of elements in the array as necessary to determine what order they should be in. That implementation of the comparison function is not atypical when dealing with a numeric sort. Details in the spec or on some other more readable sites.

5

Is the array sort callback function called many times during the course of the sort?

Since this is a comparison sort, given N items, the callback function should be invoked on average (N * Lg N) times for a fast sort like Quicksort. If the algorithm used is something like Bubble Sort, then the callback function will be invoked on average (N * N) times.

The minimum number of invocations for a comparison sort is (N-1) and that is only to detect an already sorted list (i.e. early out in Bubble Sort if no swaps occur).

-1

Is the array sort callback function called many times during the course of the sort?

Yes

If so, I'd like to know which two numbers are passed into the function each time.

a: The first element for comparison.

b: The second element for comparison.

In the following example, a will be "2" and b will be "3" in the first iteration

How are the two sets of numbers then sorted in relation to one another?

Elements are sorted according to the return value of the compare function.

greater than 0: sort a after b

less than 0: sort a before b

equal to 0: keep original order of a and b

Here is an example

var arr = [3, 2, 1, 5, 4, 6, 7, 9, 8, 10];
console.log(arr.sort((a, b) => {
  console.log(a - b, a, b);
  //b-a if sorting in decending order
  return a - b; 
}));

0

Not the answer you're looking for? Browse other questions tagged or ask your own question.