Sorting Arrays Correctly

Med

Array sorting in JavaScript has a critical gotcha: the default sort() converts elements to strings! This causes unexpected behavior with numbers. Understanding comparator functions is essential for correct sorting. This concept covers numeric sorting, string sorting with locale awareness, object sorting by properties, and the new immutable toSorted() method.

Interactive Visualization

mutation Methods

1
2
3

Key Points

  • sort() converts elements to strings by default!
  • Comparator: (a, b) => negative if a < b, positive if a > b, 0 if equal
  • Numeric sort: arr.sort((a, b) => a - b)
  • String sort: use localeCompare for proper Unicode
  • Object sort: compare object properties in comparator
  • toSorted() ES2023 - immutable version

Code Examples

The Number Sorting Trap

const nums = [10, 2, 30, 1, 15];

// ❌ WRONG: Default sort (converts to strings!)
nums.sort();
console.log(nums);  // [1, 10, 15, 2, 30] - wrong!

// Why? String comparison: "10" < "2" (lexicographic)

// ✅ CORRECT: Numeric comparator
const nums2 = [10, 2, 30, 1, 15];
nums2.sort((a, b) => a - b);
console.log(nums2);  // [1, 2, 10, 15, 30] - correct!

// How comparator works:
// if a - b < 0: a comes first
// if a - b > 0: b comes first
// if a - b === 0: keep order

sort() converts to strings. Always provide comparator for numbers: (a, b) => a - b

Descending Order

const nums = [3, 1, 4, 1, 5];

// Descending: b - a (reverse order)
nums.sort((a, b) => b - a);
console.log(nums);  // [5, 4, 3, 1, 1]

// Or negate ascending
nums.sort((a, b) => -(a - b));

// Remember: positive result = b comes first

For descending order, reverse the comparison: (a, b) => b - a

Sorting Objects by Property

const users = [
  { name: 'Alice', age: 30 },
  { name: 'Bob', age: 25 },
  { name: 'Carol', age: 35 }
];

// Sort by age
users.sort((a, b) => a.age - b.age);
console.log(users.map(u => u.name));  // ['Bob', 'Alice', 'Carol']

// Sort by name (string)
users.sort((a, b) => a.name.localeCompare(b.name));
console.log(users.map(u => u.name));  // ['Alice', 'Bob', 'Carol']

// Multi-field sort (age desc, then name asc)
users.sort((a, b) => {
  if (b.age !== a.age) {
    return b.age - a.age;  // Age descending
  }
  return a.name.localeCompare(b.name);  // Name ascending
});

Compare object properties in comparator. Use localeCompare for strings.

Immutable Sorting (ES2023)

const original = [3, 1, 4, 1, 5];

// ❌ OLD: Mutates original
const sorted = [...original].sort((a, b) => a - b);

// ✅ NEW: toSorted() (ES2023)
const sorted2 = original.toSorted((a, b) => a - b);

console.log(original);   // [3, 1, 4, 1, 5] - unchanged!
console.log(sorted2);    // [1, 1, 3, 4, 5]

// Other immutable methods
const reversed = original.toReversed();
const spliced = original.toSpliced(1, 2, 'x', 'y');
const replaced = original.with(2, 'new');

// All return new arrays, leave original unchanged

ES2023 adds toSorted(), toReversed(), toSpliced(), and with() for immutable array operations.

Common Mistakes

  • Using default sort() on numbers
  • Forgetting sort mutates the original array
  • Not using localeCompare for international strings
  • Returning boolean from comparator instead of number

Interview Tips

  • Always remember: default sort converts to strings!
  • Know the numeric comparator: (a, b) => a - b
  • Know how to sort objects by property
  • Know about toSorted() for immutable sorting