Sorting

Tutorial

Visualizations commonly have a notion of sorted order. We want to know which stock in our portfolio had the best (or worst) earnings. We want to know that heart disease is the number one cause of death in America. Other times the categories have an inherent ordering. January 2011 comes before February 2012 and "a" comes before "b". In any case, Javascript provides the Array.sort() function for sorting data in arrays. This function mutates the existing array into sorted order.

var array = [30, 3, 50, 5, "fifty"];
array.sort();
// array = [3, 30, 5, 50, "fifty"]

Array.sort() also takes an optional sorting function that allows you to specify how to sort the elements of the array. By default, Array.sort() converts each element in the array to a string and performs a string or lexicographic sort. Lexicographic sort means that each character from the two strings are compared one by one from left to right. For example '10' comes before '2' in lexicographic order because '1' is before '2'. The same holds true for every other number that starts with '1'. They all come before '2'. This is not what we want when sorting numbers.

The optional sort function has the contract that it takes two elements, say a and b, of the array and returns -1, 1 or 0 if a is before b, after b, or equal to b respectively.

// sort numbers as we expect
// smaller numbers go before larger numbers
function numSort(a,b) {
  if (a < b) {
    return -1;
  }
  if (b < a) {
    return 1;
  }
  return 0;
}

var array = [3, 30, 5, 50];
array.sort(numSort);
// array = [3, 5, 30, 50]

As a convenience, D3 provides two functions d3.ascending(a,b) and d3.descending(a,b) that sort numeric arrays in their standard ordering without effort on your part.

// 1 === d3.descending(1,3)
// i.e. 1 comes after 3

var array = [3, 30, 5, 50];
array.sort(d3.descending);
// array = [50, 30, 5, 3]

// 1 === d3.ascending(3,1)
// i.e. 3 comes after 1

array.sort(d3.ascending);
// array = [3, 5, 30, 50]

If you are sorting and array of objects, you need to specify a sorting function. The most common is just sorting on a single field, in which case it would look like the following example.

Airports with the most flights:

var data = [ {airport: "SJC", numFlights: 100},
             {airport: "LAX", numFlights: 353},
             {airport: "SNA", numFlights: 20} ];

data.sort(
function(a,b) { 
  // save time and effort by making use of d3.descending
  return d3.descending(a.numFlights, b.numFlights);
});

d3.select("#example3")
  .append("ol")
  .selectAll("li")
  .data(data)
  .enter()
  .append("li")
  .text(function(d) { return d.airport + " - " + d.numFlights; });

In this example the data is sorted highest to lowest (descending) based on the numFlights field. The code is simplified and easier to read by use of d3.descending(a,b).

It is also fairly simple to sort on multiple criteria. In the example below I sort on number of flights descending first and then alphabetically by airport code second.

Airports with the most flights:

var data = [ {airport: "SJC", numFlights: 100},
             {airport: "ABQ", numFlights: 353},  // just for the sake of example
             {airport: "LAX", numFlights: 353},
             {airport: "ZZV", numFlights: 20},   // just for the sake of example
             {airport: "SNA", numFlights: 20} ];

data.sort(
function(a,b) { 
  var numComp = d3.descending(a.numFlights, b.numFlights);
  if (numComp === 0) {                                     // only when 1st undecided
    return d3.ascending(a.airport, b.airport);             // 2nd sort criteria
  }
  return numComp;
});

d3.select("#example4")
  .append("ol")
  .selectAll("li")
  .data(data)
  .enter()
  .append("li")
  .text(function(d) { return d.airport + " - " + d.numFlights; });

Quiz

In Javascript's default sort order 457 comes before 91. True/False.

In a sort callback, returning -1 means that the first parameter is
greater than the second. True/False.

When sorting on multiple criteria, you only have to check the second
criteria when the first criteria is not equal. True/False.

Things to do

  1. Change the code below so that the list of states is sorted alphabetically by name.

Extra Credit

Change the code on the left. Once you've made a change, the page will render on the right.