JavaScriptIntermediate

JavaScript Array sort, reverse, flat and flatMap Explained

Learn to sort arrays correctly in JavaScript with custom compare functions, reverse them, and flatten nested arrays with flat and flatMap, with real examples.

All JavaScript lessons

What you will learn

Lists need order. A shop shows the cheapest product first, a leaderboard shows the highest score first, a contact list is alphabetical. Sometimes your data is also a list of lists that you want to turn into a single list. In this lesson you will learn how sort really works (and why it breaks numbers by default), how to write custom compare functions for numbers, text, objects and multiple keys, how reverse works, and how flat and flatMap flatten nested arrays.

sort: the basics

const fruits = ["mango", "apple", "banana"];

fruits.sort();

console.log(fruits);   // ["apple", "banana", "mango"]

Two very important facts:

  1. sort changes the original array (it is a mutating method). It also returns the same array.
  2. By default, it sorts everything as text, in dictionary order of the character codes.

The number trap

Because the default is text order, numbers behave strangely:

console.log([10, 9, 1, 100, 25].sort());
// [1, 10, 100, 25, 9]

Why? It compares the numbers as text: "1" comes before "10", which comes before "100", then "25", and finally "9". Letters are compared one at a time, so "9" is “bigger” than "25" because "9" is bigger than "2".

Capital letters come first

console.log(["banana", "Apple", "cherry"].sort());
// ["Apple", "banana", "cherry"]

console.log(["b", "a", "C"].sort());
// ["C", "a", "b"]  (uppercase letters sort before lowercase ones)

Both traps have the same cure: a compare function.

The compare function

sort accepts a callback with two parameters, a and b, which are two items being compared. Your function must return a number:

Return value Meaning
Negative (less than 0) a should come before b
Positive (greater than 0) b should come before a
Zero Keep their order as it is

Numbers: ascending and descending

const numbers = [10, 9, 1, 100, 25];

// Smallest to largest
console.log([...numbers].sort((a, b) => a - b));   // [1, 9, 10, 25, 100]

// Largest to smallest
console.log([...numbers].sort((a, b) => b - a));   // [100, 25, 10, 9, 1]

Why does a - b work? If a is smaller than b, then a - b is negative, so a goes first. If a is bigger, the result is positive, so b goes first. For descending order, just swap them: b - a.

Memory trick: a - b gives ascending order (small to large). Swap the letters, b - a, for descending order.

Copy first if you need the original

Since sort changes the array, copy it before sorting when you still need the old order:

const scores = [72, 95, 60];

const sorted = [...scores].sort((a, b) => b - a);

console.log(sorted);   // [95, 72, 60]
console.log(scores);   // [72, 95, 60]  (still in the original order)

Newer JavaScript has toSorted(), which does the copy for you:

const scores = [72, 95, 60];

const sorted2 = scores.toSorted((a, b) => b - a);

console.log(sorted2);   // [95, 72, 60]
console.log(scores);    // [72, 95, 60]

Text: use localeCompare

For alphabetical sorting that respects language rules, use localeCompare. It returns a negative, zero or positive number, exactly what sort wants:

const names = ["Zara", "amit", "Bina", "Éric", "chirag"];

console.log([...names].sort());
// ["Bina", "Zara", "amit", "chirag", "Éric"]   (capitals first, accents last)

console.log([...names].sort((a, b) => a.localeCompare(b)));
// ["amit", "Bina", "chirag", "Éric", "Zara"]   (a proper dictionary order)

Sorting text that contains numbers (natural sort)

const files = ["file10.txt", "file2.txt", "file1.txt"];

console.log([...files].sort());
// ["file1.txt", "file10.txt", "file2.txt"]  (10 before 2: wrong for humans)

console.log(
  [...files].sort((a, b) => a.localeCompare(b, undefined, { numeric: true }))
);
// ["file1.txt", "file2.txt", "file10.txt"]

The option { numeric: true } tells localeCompare to read digits as numbers.

Sorting objects by a property

const products = [
  { name: "Headphones", price: 1500 },
  { name: "Pen", price: 20 },
  { name: "Backpack", price: 900 }
];

// By price, cheapest first
const byPrice = [...products].sort((a, b) => a.price - b.price);
console.log(byPrice.map((p) => p.name));   // ["Pen", "Backpack", "Headphones"]

// By name, A to Z
const byName = [...products].sort((a, b) => a.name.localeCompare(b.name));
console.log(byName.map((p) => p.name));    // ["Backpack", "Headphones", "Pen"]

Sorting by more than one key

Sort by category first, and use the price when the categories are the same. The trick is that || moves to the next rule when the first result is 0:

const items = [
  { name: "Mouse", category: "electronics", price: 600 },
  { name: "Pen", category: "stationery", price: 20 },
  { name: "Headphones", category: "electronics", price: 1500 },
  { name: "Notebook", category: "stationery", price: 80 }
];

const sorted = [...items].sort(
  (a, b) => a.category.localeCompare(b.category) || a.price - b.price
);

console.log(sorted.map((i) => i.name));
// ["Mouse", "Headphones", "Pen", "Notebook"]

A custom order

Sometimes the order is not alphabetical or numeric. Define it with an array and compare positions:

const tasks = [
  { title: "Fix bug", priority: "medium" },
  { title: "Write test", priority: "low" },
  { title: "Deploy", priority: "high" }
];

const order = ["high", "medium", "low"];

tasks.sort((a, b) => order.indexOf(a.priority) - order.indexOf(b.priority));

console.log(tasks.map((t) => t.title));   // ["Deploy", "Fix bug", "Write test"]

Sorting dates

Dates written in the ISO format (2026-10-01) sort correctly as text. For real Date objects, subtract them:

const posts = [
  { title: "A", date: "2026-09-25" },
  { title: "B", date: "2026-10-01" },
  { title: "C", date: "2026-08-10" }
];

// Newest first
const newest = [...posts].sort((a, b) => new Date(b.date) - new Date(a.date));

console.log(newest.map((p) => p.title));   // ["B", "A", "C"]

(Dates have their own lesson later.)

Other things to know

  • Stable sort: items that compare as equal keep their original order. Modern JavaScript guarantees this.
  • undefined goes last: [3, undefined, 1].sort() gives [1, 3, undefined], and your compare function is not called for it.
  • A wrong compare function (for example one that returns only true or false) gives unpredictable results. Always return a number.

reverse: flip the order

reverse() turns the array around. Like sort, it changes the original array.

const letters = ["a", "b", "c", "d"];

letters.reverse();
console.log(letters);   // ["d", "c", "b", "a"]

To keep the original, copy first (or use the newer toReversed()):

const original = [1, 2, 3];

const flipped = [...original].reverse();

console.log(flipped);    // [3, 2, 1]
console.log(original);   // [1, 2, 3]

reverse is not the same as sorting descending

reverse just flips whatever order exists. It does not sort:

console.log([3, 1, 2].reverse());                  // [2, 1, 3]  (flipped, not sorted)
console.log([3, 1, 2].sort((a, b) => b - a));      // [3, 2, 1]  (sorted, largest first)

A classic use is reversing text:

console.log("hello".split("").reverse().join(""));   // "olleh"

flat: flatten nested arrays

Sometimes you have arrays inside arrays. flat pulls the inner items out into one array. It returns a new array.

const nested = [1, [2, 3], [4, 5]];

console.log(nested.flat());   // [1, 2, 3, 4, 5]

Depth

By default, flat goes only one level deep. Give it a depth to go deeper:

const deep = [1, [2, [3, [4]]]];

console.log(deep.flat());           // [1, 2, [3, [4]]]  (one level)
console.log(deep.flat(2));          // [1, 2, 3, [4]]    (two levels)
console.log(deep.flat(Infinity));   // [1, 2, 3, 4]      (all levels)

flat also removes empty holes

console.log([1, , 3].flat());   // [1, 3]

flatMap: map, then flatten

flatMap runs a map and then flattens the result by one level. It is useful when each item turns into several items (or none).

Compare:

const sentences = ["Hello world", "I love JS"];

// map gives an array of arrays
console.log(sentences.map((s) => s.split(" ")));
// [["Hello", "world"], ["I", "love", "JS"]]

// flatMap gives one flat array
console.log(sentences.flatMap((s) => s.split(" ")));
// ["Hello", "world", "I", "love", "JS"]

Filter and transform in one step

If your callback returns an empty array [] for an item, that item disappears. So flatMap can filter and map at the same time:

const inputs = ["10", "abc", "20", "x"];

const numbers = inputs.flatMap((text) => {
  const n = Number(text);
  return Number.isNaN(n) ? [] : [n];   // skip bad values, wrap good ones
});

console.log(numbers);   // [10, 20]

Make several items from one

const orders = [
  { item: "Pen", qty: 2 },
  { item: "Book", qty: 1 }
];

const labels = orders.flatMap((o) => Array(o.qty).fill(o.item));
console.log(labels);   // ["Pen", "Pen", "Book"]

(Array(2).fill("Pen") makes ["Pen", "Pen"].)

Real-life use cases

1. Leaderboard: top 3 players

const players = [
  { name: "Riya", score: 820 },
  { name: "Karan", score: 950 },
  { name: "Neha", score: 760 },
  { name: "Aman", score: 890 }
];

const top3 = [...players]
  .sort((a, b) => b.score - a.score)
  .slice(0, 3)
  .map((p, i) => `${i + 1}. ${p.name} (${p.score})`);

console.log(top3);
// ["1. Karan (950)", "2. Aman (890)", "3. Riya (820)"]

2. A table that sorts by any column

function sortBy(list, key, direction = "asc") {
  const sorted = [...list].sort((a, b) => {
    if (typeof a[key] === "string") {
      return a[key].localeCompare(b[key]);
    }
    return a[key] - b[key];
  });

  return direction === "desc" ? sorted.reverse() : sorted;
}

const students = [
  { name: "Riya", marks: 82 },
  { name: "Karan", marks: 67 },
  { name: "Neha", marks: 91 }
];

console.log(sortBy(students, "marks", "desc").map((s) => s.name));   // ["Neha", "Riya", "Karan"]
console.log(sortBy(students, "name").map((s) => s.name));            // ["Karan", "Neha", "Riya"]

One function powers a “click a column header to sort” feature.

3. Newest posts first

const posts = [
  { title: "Closures", date: "2026-09-25" },
  { title: "Arrays", date: "2026-10-01" },
  { title: "Loops", date: "2026-08-10" }
];

const latest = [...posts].sort((a, b) => b.date.localeCompare(a.date));
console.log(latest.map((p) => p.title));   // ["Arrays", "Closures", "Loops"]

4. Collect all tags from a list of posts

const posts = [
  { title: "Arrays", tags: ["js", "arrays"] },
  { title: "Closures", tags: ["js", "functions"] },
  { title: "Loops", tags: ["js", "basics"] }
];

const allTags = posts.flatMap((post) => post.tags);
console.log(allTags);
// ["js", "arrays", "js", "functions", "js", "basics"]

const uniqueTags = [...new Set(allTags)].sort();
console.log(uniqueTags);   // ["arrays", "basics", "functions", "js"]

5. Flatten a menu with sub-menus

const menu = [
  ["Home"],
  ["Courses", ["HTML", "CSS", "JavaScript"]],
  ["Contact"]
];

console.log(menu.flat(Infinity));
// ["Home", "Courses", "HTML", "CSS", "JavaScript", "Contact"]

6. Words in a text, sorted and without duplicates

const text = "the cat and the hat and the bat";

const words = [...new Set(text.split(" "))].sort();
console.log(words);   // ["and", "bat", "cat", "hat", "the"]

7. Median of a list

const values = [7, 1, 9, 3, 5];

const sorted = [...values].sort((a, b) => a - b);
const middle = Math.floor(sorted.length / 2);

const median =
  sorted.length % 2 === 0
    ? (sorted[middle - 1] + sorted[middle]) / 2
    : sorted[middle];

console.log(median);   // 5

8. Shuffle a list (the right way)

You may see array.sort(() => Math.random() - 0.5) online. It looks clever, but it gives biased results, so some orders appear more often than others. Use the Fisher-Yates method instead:

function shuffle(array) {
  const copy = [...array];

  for (let i = copy.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [copy[i], copy[j]] = [copy[j], copy[i]];   // swap two items
  }

  return copy;
}

console.log(shuffle([1, 2, 3, 4, 5]));   // for example [3, 5, 1, 4, 2]

The line with the square brackets swaps two items using destructuring, which you will learn soon.

9. Sort by the length of words

const words = ["banana", "kiwi", "apple", "fig"];

const byLength = [...words].sort((a, b) => a.length - b.length);
console.log(byLength);   // ["fig", "kiwi", "apple", "banana"]

Common mistakes

  • Sorting numbers without a compare function. [10, 9, 1].sort() gives [1, 10, 9]. Use (a, b) => a - b.
  • Forgetting that sort and reverse change the original. Copy first with [...array], or use toSorted() / toReversed().
  • Writing a compare function that returns true or false. It must return a number (negative, zero or positive).
  • Using a > b instead of a - b as the compare function. It does not work reliably.
  • Sorting text with capital letters using the default. Use localeCompare.
  • Using reverse and expecting it to sort. It only flips the current order.
  • Using a.price - b.price when the values are text. Convert them with Number() first, or the result may be NaN.
  • Using sort(() => Math.random() - 0.5) to shuffle. It is biased. Use Fisher-Yates.
  • Forgetting that flat() goes one level deep. Give a depth, or Infinity.
  • Using flatMap for deeper flattening. It flattens only one level.
  • Sorting an array of objects by a property that is missing on some items. The result can be NaN, and the order becomes unpredictable. Give the items a default value first.

Practice

  1. Sort [40, 5, 100, 25, 1] in ascending and in descending order, without changing the original array.
  2. Sort ["pear", "Apple", "banana", "cherry"] alphabetically, ignoring the difference between capital and small letters.
  3. Sort a list of 4 books (title, pages) by number of pages, from the thickest to the thinnest.
  4. Sort the same books by title A to Z.
  5. Given a list of students with grade (“A”, “B”, “C”) and name, sort them by grade and then by name.
  6. Reverse the array [1, 2, 3, 4, 5], first changing the original, and then keeping the original untouched.
  7. Flatten [1, [2, [3, [4, [5]]]]] to [1, 2, 3, 4, 5].
  8. Use flatMap to turn ["a b", "c d e"] into ["a", "b", "c", "d", "e"].
  9. Use flatMap to double only the even numbers of [1, 2, 3, 4], so the result is [4, 8].
  10. Challenge: write sortByMany(list, rules) that sorts by several keys, for example [["category", "asc"], ["price", "desc"]].

Recap

  • sort() sorts in place and, by default, as text. Always pass a compare function for numbers: (a, b) => a - b for ascending, (a, b) => b - a for descending.
  • The compare function returns a negative number (a first), positive (b first) or zero.
  • Use localeCompare for text (with { numeric: true } for names like file10), a.price - b.price for objects, and || to sort by several keys.
  • sort and reverse change the original array. Copy first with [...array], or use toSorted() and toReversed().
  • reverse() only flips the order. It does not sort.
  • flat(depth) flattens nested arrays (one level by default, Infinity for all levels) and returns a new array.
  • flatMap(fn) is map followed by a one-level flat. Return [] to drop an item, or an array to create several.
  • Do not shuffle with sort(() => Math.random() - 0.5). Use Fisher-Yates.
  • Next you will learn objects: properties, methods and the way real data is organised.