03-javascriptTermsLevel_04sort / reverse

sort / reverse

Level 4 — Iteration & Array Methods Order elements (with comparator) / reverse order.


1. Prerequisites

  • Array — A high-level, list-like object.
  • Callback Function — A function passed as an argument to be executed later.

2. Term Category

Language Core (Universal: Works everywhere): sort / reverse is a fundamental concept in this technology stack. Level 4 — Iteration & Array Methods


3. Explanation

(1) Design Motivation — "Why did we design this?"

Organizing collections of data—sorting products by price, listing users alphabetically, or reversing a search results feed—is a daily programming requirement. JavaScript provides .sort() and .reverse() for this.

However, they come with two major design caveats:

  1. They are mutating methods: They modify the original array in-place rather than returning a sorted copy.
  2. Default lexicographical sort: By default, .sort() converts all elements to string representations and compares their UTF-16 code unit values. This works fine for words, but fails catastrophically for numbers (e.g. 10 is sorted before 2 because the character "1" comes before "2" alphabetically).

To sort numbers or complex structures correctly, you must supply a comparator callback function to define custom sorting logic.

(2) Reality Metaphor

Imagine a clerk sorting folders in a cabinet.

  • Default .sort() is like a clerk who sorts files purely by the alphabetical spellings of their labels. If they see a folder labeled "10" and a folder labeled "2", they file "10" first because "1" comes before "2".
  • A Comparator Callback is like giving the clerk a rules sheet. You hand them folders A and B and tell them: "Compare their mathematical weights. If the weight of A minus B is positive, slide A behind B."

(3) JavaScript Code Examples

Short Snippet

const fruits = ["Banana", "Apple", "Cherry"];
fruits.sort(); // Mutates fruits to ["Apple", "Banana", "Cherry"]
fruits.reverse(); // Mutates fruits to ["Cherry", "Banana", "Apple"]

console.log(fruits); // ["Cherry", "Banana", "Apple"]

Fuller Example

// A product price sorting catalog showing the number sorting bug and custom sorting
const prices = [100, 5, 25, 10, 2];

// 1. The default sort bug:
prices.sort();
console.log("Lexicographical (bugged) Sort:", prices); 
// [ 100, 10, 2, 25, 5 ] (because "1" < "2" < "5" alphabetically!)

// 2. The correct way: pass a comparator callback function
// The comparator receives pairs (a, b) and expects:
// - A negative value if 'a' should come before 'b'
// - A positive value if 'b' should come before 'a'
// - Zero if they are equal
prices.sort(function(a, b) {
  return a - b; // Ascending sort helper formula
});
console.log("Numeric Ascending Sort:", prices); // [ 2, 5, 10, 25, 100 ]

// 3. Sorting an array of objects by a key
const products = [
  { name: "Laptop", price: 999 },
  { name: "Mouse", price: 25 },
  { name: "Keyboard", price: 75 }
];

// Sort products descending by price (b - a)
products.sort((a, b) => b.price - a.price);
console.log("Products (Expensive first):", products);
// [ { name: 'Laptop', price: 999 }, { name: 'Keyboard', price: 75 }, { name: 'Mouse', price: 25 } ]

4. Common Mistakes & Pitfalls

Mistake 1: Sorting Numbers without a Comparator Function

The mistake: Calling prices.sort() expecting numerical sorting.

Why it's wrong: As shown in the example, default sorting coerces numbers to strings first. This will sort 100 before 2.

Incorrect:

const list = [20, 5, 100];
list.sort(); 

console.log(list); // [100, 20, 5] (Oops!)

Fix:

const list = [20, 5, 100];
list.sort((a, b) => a - b); // Ascending numeric sort

console.log(list); // [5, 20, 100]

Mistake 2: Forgetting that sort() Mutates the Array

The mistake: Sorting a function argument array directly, causing unexpected side effects on the parent data reference.

Why it's wrong: Since .sort() mutates the original reference in-place, the caller's array will be modified permanently.

Incorrect:

const data = [3, 1, 2];
const sorted = data.sort(); // Mutates 'data'!

console.log(data); // [1, 2, 3] (Original array was changed!)

Fix:

const data = [3, 1, 2];

// Use spread syntax to clone the array before sorting
const sorted = [...data].sort();

console.log(data);   // [3, 1, 2] (Safe!)
console.log(sorted); // [1, 2, 3]

Mistake 3: Unhandled Asynchronous Failures in Sort Reverse Operations

The mistake: Executing asynchronous operations within Sort Reverse without wrapping await calls in try...catch blocks or chaining .catch().

Why it's wrong: Unhandled promise rejections trigger UnhandledPromiseRejectionWarning in Node.js or unhandled rejection errors in modern browsers, leaving application state in corrupted or uncoordinated states.

Incorrect:

async function processData() {
    const res = await fetch("/api/sort_reverse"); // ❌ Unhandled network failure crashes execution flow
    const data = await res.json();
    return data;
}

Fix:

async function processData() {
    try {
        const res = await fetch("/api/sort_reverse");
        if (!res.ok) throw new Error(`HTTP Error: ${res.status}`);
        return await res.json();
    } catch (err) {
        console.error(`Caught error in sort_reverse: ${err.message}`);
        return null;
    }
}

5. Practice Exercises

Exercise 1: Multi-Attribute Product Sorting & Non-Mutating Reversal

Scenario: An e-commerce catalog engine sorts products by price using custom comparator functions and creates reversed arrays without mutating original collections.

Requirements:

  1. Write sortProductsByPrice(products, ascending).
  2. Use non-mutating copy […products].sort((a, b) ≥ …).
  3. Support reverse direction.
  4. Return sorted array.
Answer

Implementation

function sortProductsByPrice(products, ascending = true) {
  if (!Array.isArray(products)) return [];

  const copy = [...products];
  copy.sort((a, b) => {
    return ascending ? a.price - b.price : b.price - a.price;
  });
  return copy;
}

// Verification tests
const items = [{ name: "B", price: 20 }, { name: "A", price: 10 }];
const sorted = sortProductsByPrice(items, true);
console.assert(sorted[0].price === 10, "Test 1 Failed");
console.assert(items[0].price === 20, "Test 2 Failed: Original array mutated");

Technical Explanation

  1. Numeric Comparator Requirement: Default .sort() converts elements to strings; always provide custom numeric comparator (a, b) ≥ a - b.
  2. In-Place Mutation Warning: Array.prototype.sort() and reverse() mutate target array in place; always copy array before sorting.
  3. Comparator Return Conventions: Return negative if a < b, positive if a > b, 0 if equal.


7. Key Takeaways

  • .sort() and .reverse() modify the original array reference in-place (mutating).
  • By default, .sort() orders elements alphabetically by converting them to strings, which breaks numeric sorting.
  • To sort numbers or objects, always pass a comparator callback: (a, b) => a - b (for ascending) or (b - a) (for descending).
  • Clone arrays using spread syntax [...array].sort(...) to prevent mutating shared data references.
Built with LogoFlowershow