JavaScript: Array.sort()

Array.sort() Basic

sort()
The sort() method sorts the elements of an array in place and returnes the sorted array.
sort(compareFn)
  • Sorting happens in original array
    It doesn't create a new array. Original array got sorted.
  • Sort according to compare function
  • Return original sorted array
    If assigned into a variable, be careful. The variable is only a pointer to the original array, not an actually new array. There's only 1 array by the end. The sorted original array.
  • Variable assigned is only a pointer
    Assigned sort result to a new variable? Be careful, the new variable is only a pointer to the original array. No more than that. It's not a checked out state of the array.
Syntax of sort()
// Functionless (default string sort)
sort()

// Compare function
sort(compareFn)

// Inline compare function
sort(function compareFn(a, b) {...})

// Arrow function
sort((a, b) => {...})
Parameter (1):
Compare function (optional)
Return value:
Pointer to the sorted array
Original array:
Sorted
Compare function
function compareFn(a, b) {
    ...
    return number;
}
// Switch elements if return is positive.
Parameters (2):
  • a : first element to compare
  • b : second element to compare
Return values: Number
  • < 0 : sort a before b
  • > 0 : sort b before a
  • = 0 : keep original order
Remember:
  • Switch if positive!
  • When it's negative or 0, it's already correct.

Functionless string sort

Functionless sort(): "string sort"
When compare function omitted: Sort according to UTF-16 code units order
  • Elements are converted to strings
  • Strings are compared in UTF-16 code units order.
  • In general: 0-9, to A-Z, to a-z.
  • Element's convertion to string is only for sorting purpose. The actual array elements values remain whatever they were.
Ascending string sort ["a", "b", "c", ...]
array.sort()
Descending string sort ["z", "y", "x", ...].
array.sort().reverse()
First, sort ascending as usual, then, reverse array order.
  • reverse(): Reversing the order of an array in place.
    const array = [1, 30, 5, 70, 9];
    array.reverse()     // array = [9, 70, 5, 30, 1];
    

Numeric sort

Ascending numeric sort [1, 2, 3, ...]
array.sort((a, b) => a - b)
Pseudocodes journey for (a, b):
  • It needs switching only if b < a
  • If b < a, switch.
    • if (b < a) {return 1}
    • else {return -1}
  • In ternary:
    • b < a? 1 : -1
  • At this point, the code is becoming:
    array.sort((a, b) => b < a? 1 : -1)
  • But really, we want to say: switch if b < a
    • Switch = return positive.
    • How to make a positive value if b < a? By returning a - b!
    • Returning a - b means:
      • When a < b, no switching needed. Return value is negative, a is placed before b as before.
      • When a = b, no switching needed. Return value is 0, a and b are put back as is.
      • When b < a, then they need to be switched. Return value is positive, then the switching happened.
  • Therefore, the code:
    array.sort((a, b) => a - b)
Descending numeric sort [9, 8, 7, ...]
array.sort((a, b) => b - a)
Pseudocodes journey for (a, b):
  • Similar, but now: switch if a < b
  • Switch is if return is positive.
  • To make positive from a and b when a is smaller is: b - a
  • Therefore, the code.
String numbers
This sort will work even when numbers are written as string (between quotation marks), since JavaScript treated string numbers as numbers in mathematical operations.

Object array sort

Array example
const array = [
    { name: "jen", age: 3 },
    { name: "Edi", age: 7 },
    { name: "Zed", age: 8 },
    { name: "abe", age: 5 }
];
Ascending sort based on age
  • Just like in numeric sort
array.sort((a, b) => a.age - b.age)
array = [
    { name: "jen", age: 3 },
    { name: "abe", age: 5 },
    { name: "Edi", age: 7 },
    { name: "Zed", age: 8 }
];
Ascending sort based on name
  • Unfortunately can't just use a.name - b.name because each of them is NaN and so is the result.
  • However a.name and b.name still can be compared using <, >, and ===.
  • array.sort((a, b) => a.name > b.name? 1 : -1)
  • It works except it is case-sensitive.
  • To make it case insensitive, let's compare their lowerCase or upperCase values instead.
  • array.sort((a, b) =>
        a.name.toLowerCase() > b.name.toLowerCase()? 1 : -1
    )
    
array = [
    { name: "abe", age: 5 },
    { name: "Edi", age: 7 },
    { name: "jen", age: 3 },  
    { name: "Zed", age: 8 }
];
Equal value and zero return
We can omit zero notion in return value for 2 possible reasons:
  • zero's if-condition is grouped with negative's if-condition, because both means the same thing: Don't switch.
  • It's okay if when zero's if-condition got accidentally grouped with positive's if-condition, because the 2 elements have identical value, and switching their position doesn't matter.
Group the equal's if-condition with the negative's if condition for no switch
However in more complicated array, for example object array, the array elements are more likely not identical. Their defining value could be equal, but the rest of the values could be different. In this case, we have to be careful grouping the equal if-condition with the negative if-condition if we don't want them to get switched.
Specified separately
Or... it can be specified separately.
Sort stability
If equation is written properly, then now, after EcmaScript 2019, when defining value is equal, the order will remain the same. A stable sorting algorithm guarantees this. Before, they are not guaranted and the result can be switched.
When is this useful? Say you have an object array of student names and grades. The array is already in alphabetical order based on the student names. But now we want to sort it based on the grade first. Then of course name should be still in alphabetical order following the grade sort. In the past, they can come back shuffled. Original order not maintained.

Non-ASCII character sort

localeCompare()
array.sort((a, b) => a.localeCompare(b))
array = ['réservé', 'premier', 'communiqué', 'café', 'adieu', 'éclair'];
array.sort((a, b) => a.localeCompare(b));
array;
// ['adieu', 'café', 'communiqué', 'éclair', 'premier', 'réservé']

Sorting with map

Future learning

>

Wesbos Javascript30: 04 Array Cardio Practice

Task: 3. Sort the inventors by birthdate, oldest to youngest
Given
const inventors = [
    { first: 'Albert', last: 'Einstein', year: 1879, passed: 1955 },
    { first: 'Isaac', last: 'Newton', year: 1643, passed: 1727 },
    { first: 'Galileo', last: 'Galilei', year: 1564, passed: 1642 },
    { first: 'Marie', last: 'Curie', year: 1867, passed: 1934 },
    { first: 'Johannes', last: 'Kepler', year: 1571, passed: 1630 },
    { first: 'Nicolaus', last: 'Copernicus', year: 1473, passed: 1543 },
    { first: 'Max', last: 'Planck', year: 1858, passed: 1947 },
    { first: 'Katherine', last: 'Blodgett', year: 1898, passed: 1979 },
    { first: 'Ada', last: 'Lovelace', year: 1815, passed: 1852 },
    { first: 'Sarah E.', last: 'Goode', year: 1855, passed: 1905 },
    { first: 'Lise', last: 'Meitner', year: 1878, passed: 1968 },
    { first: 'Hanna', last: 'Hammarström', year: 1829, passed: 1909 }
];
Solution
const invBirth = inventors.sort((a, b) => a.year - b.year)
Result
invBirth;
// (12) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
    0: {first: 'Nicolaus', last: 'Copernicus', year: 1473, passed: 1543}
    1: {first: 'Galileo', last: 'Galilei', year: 1564, passed: 1642}
    2: {first: 'Johannes', last: 'Kepler', year: 1571, passed: 1630}
    3: {first: 'Isaac', last: 'Newton', year: 1643, passed: 1727}
    4: {first: 'Ada', last: 'Lovelace', year: 1815, passed: 1852}
    5: {first: 'Hanna', last: 'Hammarström', year: 1829, passed: 1909}
    6: {first: 'Sarah E.', last: 'Goode', year: 1855, passed: 1905}
    7: {first: 'Max', last: 'Planck', year: 1858, passed: 1947}
    8: {first: 'Marie', last: 'Curie', year: 1867, passed: 1934}
    9: {first: 'Lise', last: 'Meitner', year: 1878, passed: 1968}
    10: {first: 'Albert', last: 'Einstein', year: 1879, passed: 1955}
    11: {first: 'Katherine', last: 'Blodgett', year: 1898, passed: 1979}
5. Sort the inventors by years lived
const invAscending = inventors.sort((a, b) => (a.passed - a.year) - (b.passed - b.year));

const invDescending = inventors.sort((a, b) => (b.passed - b.year) - (a.passed - a.year));
Result
invAscending;
// (12) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
    0: {first: 'Ada', last: 'Lovelace', year: 1815, passed: 1852}
    1: {first: 'Sarah E.', last: 'Goode', year: 1855, passed: 1905}
    2: {first: 'Johannes', last: 'Kepler', year: 1571, passed: 1630}
    3: {first: 'Marie', last: 'Curie', year: 1867, passed: 1934}
    4: {first: 'Nicolaus', last: 'Copernicus', year: 1473, passed: 1543}
    5: {first: 'Albert', last: 'Einstein', year: 1879, passed: 1955}
    6: {first: 'Galileo', last: 'Galilei', year: 1564, passed: 1642}
    7: {first: 'Hanna', last: 'Hammarström', year: 1829, passed: 1909}
    8: {first: 'Katherine', last: 'Blodgett', year: 1898, passed: 1979}
    9: {first: 'Isaac', last: 'Newton', year: 1643, passed: 1727}
    10: {first: 'Max', last: 'Planck', year: 1858, passed: 1947}
    11: {first: 'Lise', last: 'Meitner', year: 1878, passed: 1968}

invDescending;
// (12) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
    0: {first: 'Lise', last: 'Meitner', year: 1878, passed: 1968}
    1: {first: 'Max', last: 'Planck', year: 1858, passed: 1947}
    2: {first: 'Isaac', last: 'Newton', year: 1643, passed: 1727}
    3: {first: 'Katherine', last: 'Blodgett', year: 1898, passed: 1979}
    4: {first: 'Hanna', last: 'Hammarström', year: 1829, passed: 1909}
    5: {first: 'Galileo', last: 'Galilei', year: 1564, passed: 1642}
    6: {first: 'Albert', last: 'Einstein', year: 1879, passed: 1955}
    7: {first: 'Nicolaus', last: 'Copernicus', year: 1473, passed: 1543}
    8: {first: 'Marie', last: 'Curie', year: 1867, passed: 1934}
    9: {first: 'Johannes', last: 'Kepler', year: 1571, passed: 1630}
    10: {first: 'Sarah E.', last: 'Goode', year: 1855, passed: 1905}
    11: {first: 'Ada', last: 'Lovelace', year: 1815, passed: 1852}
7. Sort the people alphabetically by last name
Given
const people = [
    'Bernhard, Sandra', 'Bethea, Erin', 'Becker, Carl', 'Bentsen, Lloyd', 'Beckett, Samuel', 'Blake, William', 'Berger, Ric', 'Beddoes, Mick', 'Beethoven, Ludwig',
    'Belloc, Hilaire', 'Begin, Menachem', 'Bellow, Saul', 'Benchley, Robert', 'Blair, Robert', 'Benenson, Peter', 'Benjamin, Walter', 'Berlin, Irving',
    'Benn, Tony', 'Benson, Leana', 'Bent, Silas', 'Berle, Milton', 'Berry, Halle', 'Biko, Steve', 'Beck, Glenn', 'Bergman, Ingmar', 'Black, Elk', 'Berio, Luciano',
    'Berne, Eric', 'Berra, Yogi', 'Berry, Wendell', 'Bevan, Aneurin', 'Ben-Gurion, David', 'Bevel, Ken', 'Biden, Joseph', 'Bennington, Chester', 'Bierce, Ambrose',
    'Billings, Josh', 'Birrell, Augustine', 'Blair, Tony', 'Beecher, Henry', 'Biondo, Frank'
];
Sort based on the last name
const sortLastname = people.sort();
Sort based on the first name
function switchName(name) {
    const nameArr = name.split(", ");
    return `${nameArr[1]} ${nameArr[0]}`;
}

const sortFirstname = people.sort((a, b) => {
    const switchedA = switchName(a);
    const switchedB = switchName(b);
    return (switchedA > switchedB)? 1 : -1;
})
Results
sortLastname;
// (41) ['Beck, Glenn', 'Becker, Carl', 'Beckett, Samuel', 'Beddoes, Mick', 'Beecher, Henry', 'Beethoven, Ludwig', 'Begin, Menachem', 'Belloc, Hilaire', 'Bellow, Saul', 'Ben-Gurion, David', 'Benchley, Robert', 'Benenson, Peter', 'Benjamin, Walter', 'Benn, Tony', 'Bennington, Chester', 'Benson, Leana', 'Bent, Silas', 'Bentsen, Lloyd', 'Berger, Ric', 'Bergman, Ingmar', 'Berio, Luciano', 'Berle, Milton', 'Berlin, Irving', 'Berne, Eric', 'Bernhard, Sandra', 'Berra, Yogi', 'Berry, Halle', 'Berry, Wendell', 'Bethea, Erin', 'Bevan, Aneurin', 'Bevel, Ken', 'Biden, Joseph', 'Bierce, Ambrose', 'Biko, Steve', 'Billings, Josh', 'Biondo, Frank', 'Birrell, Augustine', 'Black, Elk', 'Blair, Robert', 'Blair, Tony', 'Blake, William']

sortFirstname;
// (41) ['Bierce, Ambrose', 'Bevan, Aneurin', 'Birrell, Augustine', 'Becker, Carl', 'Bennington, Chester', 'Ben-Gurion, David', 'Black, Elk', 'Berne, Eric', 'Bethea, Erin', 'Biondo, Frank', 'Beck, Glenn', 'Berry, Halle', 'Beecher, Henry', 'Belloc, Hilaire', 'Bergman, Ingmar', 'Berlin, Irving', 'Biden, Joseph', 'Billings, Josh', 'Bevel, Ken', 'Benson, Leana', 'Bentsen, Lloyd', 'Berio, Luciano', 'Beethoven, Ludwig', 'Begin, Menachem', 'Beddoes, Mick', 'Berle, Milton', 'Benenson, Peter', 'Berger, Ric', 'Benchley, Robert', 'Blair, Robert', 'Beckett, Samuel', 'Bernhard, Sandra', 'Bellow, Saul', 'Bent, Silas', 'Biko, Steve', 'Benn, Tony', 'Blair, Tony', 'Benjamin, Walter', 'Berry, Wendell', 'Blake, William', 'Berra, Yogi']

References