How to sort arrays and objects using Lodash _.sortBy() and _.orderBy() for descending order. Copy-paste examples for sorting by property, multiple fields, case insensitive, nested values, and sortBy vs orderBy differences.

Lodash _.sortBy() is a JavaScript utility function that creates a new sorted array in ascending order based on one or more sort criteria. For descending order, use _.orderBy() which accepts a direction parameter. Unlike the native Array.prototype.sort(), both methods return a new array without mutating the original and guarantee a stable sort.
// Ascending sort
_.sortBy(collection, [iteratees])
// Descending sort
_.orderBy(collection, [iteratees], ['desc'])
Here is a quick example showing both:
const users = [
{ name: "Charlie", age: 30 },
{ name: "Alice", age: 25 },
{ name: "Bob", age: 25 }
];
// Ascending by age
const ascending = _.sortBy(users, 'age');
// [{ Alice, 25 }, { Bob, 25 }, { Charlie, 30 }]
// Descending by age
const descending = _.orderBy(users, ['age'], ['desc']);
// [{ Charlie, 30 }, { Alice, 25 }, { Bob, 25 }]
Notice that Alice and Bob (both age 25) kept their original order — this is the stable sort guarantee that native .sort() didn't reliably provide before ES2019.
In this guide, you will learn how to use _.sortBy() and _.orderBy() to sort arrays of numbers, strings, and objects by single or multiple properties, nested values, dates, descending order, case-insensitive sorting, and custom logic. We also cover _.sortBy() vs _.orderBy() differences and native alternatives.
🔍 Also check out our guides on Lodash Merge and Lodash GroupBy for more data manipulation techniques.
Before using _.sortBy(), set up Lodash in your project:
Install the full library:
npm install lodash
Import _.sortBy() in your code:
// ES6 — import only sortBy (tree-shakable, reduces bundle size)
import sortBy from 'lodash/sortBy';
import orderBy from 'lodash/orderBy';
// CommonJS
const sortBy = require('lodash/sortBy');
const orderBy = require('lodash/orderBy');
// Or import the full library
import _ from 'lodash';
// then use _.sortBy() and _.orderBy()
Lightweight alternative — install only the sortBy module:
npm install lodash.sortby
import sortBy from 'lodash.sortby';
> Tip: The full Lodash library is ~70KB (minified + gzipped). Importing lodash/sortBy or installing lodash.sortby keeps your bundle lean at ~2KB.
If you are using Lodash in the browser without a bundler:
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
<script>
const sorted = _.sortBy([3, 1, 2]);
</script>
Here is the complete syntax:
_.sortBy(collection, [iteratees=[_.identity]])
| Parameter | Type | Description |
|---|---|---|
collection |
Array or Object | The collection to sort. |
[iteratees] |
Function, String, or Array | One or more sort criteria. Can be property names (strings), functions, or an array of both. |
| Returns | Array | A new sorted array. The original is not modified. |
Key difference from native .sort(): _.sortBy() never mutates the original array. It always returns a new sorted array.
The simplest use case — sorting numbers in ascending order:
const numbers = [3, 1, 4, 1, 5, 9, 2, 6];
const sorted = _.sortBy(numbers);
console.log(sorted);
// Output: [1, 1, 2, 3, 4, 5, 6, 9]
console.log(numbers);
// Output: [3, 1, 4, 1, 5, 9, 2, 6] ← original unchanged
Unlike native Array.sort(), which sorts numbers as strings by default (so 10 would come before 2), _.sortBy() handles numbers correctly out of the box.
const fruits = ["banana", "apple", "cherry", "date"];
const sorted = _.sortBy(fruits);
console.log(sorted);
// Output: ["apple", "banana", "cherry", "date"]
const words = ["apple", "banana", "cherry", "date", "elderberry"];
const sortedByLength = _.sortBy(words, 'length');
console.log(sortedByLength);
// Output: ["date", "apple", "banana", "cherry", "elderberry"]
By default, uppercase letters sort before lowercase ("Apple" comes before "banana" because uppercase A has a lower character code). To sort case-insensitively, pass a function that converts to lowercase:
const mixed = ["Banana", "apple", "Cherry", "date", "ELDERBERRY"];
const sorted = _.sortBy(mixed, (word) => word.toLowerCase());
console.log(sorted);
// Output: ["apple", "Banana", "Cherry", "date", "ELDERBERRY"]
For arrays of objects, apply the same approach to the property:
const users = [
{ name: "charlie" },
{ name: "Alice" },
{ name: "BOB" }
];
const sorted = _.sortBy(users, (user) => user.name.toLowerCase());
console.log(sorted.map(u => u.name));
// Output: ["Alice", "BOB", "charlie"]
This ensures "Alice", "alice", and "ALICE" are treated as the same value for sorting purposes.
This is the most common use case. Pass the property name as a string:
const users = [
{ name: "Charlie", age: 30 },
{ name: "Alice", age: 25 },
{ name: "Bob", age: 28 }
];
// Sort by age
const sortedByAge = _.sortBy(users, 'age');
// Output: [{ Alice, 25 }, { Bob, 28 }, { Charlie, 30 }]
// Sort by name
const sortedByName = _.sortBy(users, 'name');
// Output: [{ Alice, 25 }, { Bob, 28 }, { Charlie, 30 }]
When the first property values are equal, the second property acts as a tiebreaker. Pass an array of property names:
const students = [
{ name: "Alice", grade: "A", age: 22 },
{ name: "Bob", grade: "B", age: 22 },
{ name: "Charlie", grade: "A", age: 21 },
{ name: "Diana", grade: "B", age: 23 }
];
// Sort by grade first, then by age
const sorted = _.sortBy(students, ['grade', 'age']);
console.log(sorted);
// Output:
// [
// { name: "Charlie", grade: "A", age: 21 },
// { name: "Alice", grade: "A", age: 22 },
// { name: "Bob", grade: "B", age: 22 },
// { name: "Diana", grade: "B", age: 23 }
// ]
All "A" grade students come first (sorted by age within the group), followed by "B" grade students.
To sort by a nested property, use dot notation as a string:
const people = [
{ name: { first: "Alice", last: "Williams" }, age: 30 },
{ name: { first: "Bob", last: "Adams" }, age: 25 },
{ name: { first: "Charlie", last: "Brown" }, age: 35 }
];
const sortedByLastName = _.sortBy(people, 'name.last');
console.log(sortedByLastName.map(p => p.name.last));
// Output: ["Adams", "Brown", "Williams"]
You can also use a function for deeply nested values:
const sorted = _.sortBy(people, (person) => person.name.last);
_.sortBy() only sorts in ascending order. For descending order, use _.orderBy() which accepts a direction parameter:
const users = [
{ name: "Alice", age: 25 },
{ name: "Bob", age: 30 },
{ name: "Charlie", age: 22 }
];
// Descending by age
const sorted = _.orderBy(users, ['age'], ['desc']);
console.log(sorted);
// Output:
// [
// { name: "Bob", age: 30 },
// { name: "Alice", age: 25 },
// { name: "Charlie", age: 22 }
// ]
For simple arrays:
const numbers = [3, 1, 4, 1, 5, 9];
// Descending numbers
const sorted = _.orderBy(numbers, [], ['desc']);
console.log(sorted);
// Output: [9, 5, 4, 3, 1, 1]
_.orderBy() lets you sort different properties in different directions:
const products = [
{ name: "Widget", price: 25, rating: 4.5 },
{ name: "Gadget", price: 25, rating: 4.8 },
{ name: "Doohickey", price: 15, rating: 4.2 }
];
// Ascending by price, descending by rating
const sorted = _.orderBy(products, ['price', 'rating'], ['asc', 'desc']);
console.log(sorted);
// Output:
// [
// { name: "Doohickey", price: 15, rating: 4.2 },
// { name: "Gadget", price: 25, rating: 4.8 },
// { name: "Widget", price: 25, rating: 4.5 }
// ]
If you prefer _.sortBy(), you can reverse the result:
// Method 1: _.reverse()
const sorted = _.reverse(_.sortBy(users, 'age'));
// Method 2: Negate numeric values
const sorted = _.sortBy(users, (user) => -user.age);
// Method 3: Array.reverse()
const sorted = _.sortBy(users, 'age').reverse();
> Recommendation: Use _.orderBy() for descending sorts — it is cleaner and supports mixed directions.
This is one of the most commonly searched comparisons:
| Feature | _.sortBy() |
_.orderBy() |
|---|---|---|
| Sort direction | Ascending only | Ascending or descending (per field) |
| Syntax | _.sortBy(arr, 'age') |
_.orderBy(arr, ['age'], ['desc']) |
| Mixed directions | ❌ Not possible | ✅ ['asc', 'desc'] per field |
| Iteratees | String, Function, Array | String, Function, Array |
| Returns | New sorted array | New sorted array |
| Mutates original | No | No |
| Performance | Slightly faster (less overhead) | Same O(n log n) |
When to use which:
_.sortBy() when you only need ascending order — it has a simpler API_.orderBy() when you need descending order or mixed sort directions// These produce the same ascending result:
_.sortBy(users, 'age');
_.orderBy(users, ['age'], ['asc']);
// Only orderBy can do this:
_.orderBy(users, ['grade', 'age'], ['asc', 'desc']);
JavaScript Date objects are comparable, so _.sortBy() handles them naturally:
const events = [
{ title: "Meeting", date: new Date("2025-03-15") },
{ title: "Deadline", date: new Date("2025-01-10") },
{ title: "Launch", date: new Date("2025-02-20") }
];
// Oldest first
const sorted = _.sortBy(events, 'date');
// Newest first
const sortedDesc = _.orderBy(events, ['date'], ['desc']);
For ISO date strings, sorting works correctly because ISO format is lexicographically orderable:
const posts = [
{ title: "Post C", createdAt: "2025-03-01" },
{ title: "Post A", createdAt: "2025-01-15" },
{ title: "Post B", createdAt: "2025-02-10" }
];
const sorted = _.sortBy(posts, 'createdAt');
// Output order: Post A, Post B, Post C
Pass a function as the iteratee for complete control:
const products = [
{ name: "A", price: 100, discount: 0.1 },
{ name: "B", price: 50, discount: 0.2 },
{ name: "C", price: 80, discount: 0.05 }
];
const sorted = _.sortBy(products, (p) => p.price * (1 - p.discount));
console.log(sorted.map(p => `${p.name}: $${p.price * (1 - p.discount)}`));
// Output: ["B: $40", "C: $76", "A: $90"]
const tasks = [
{ title: "Bug fix", priority: "high", due: "2025-02-01" },
{ title: "Feature", priority: "low", due: "2025-01-15" },
{ title: "Refactor", priority: "high", due: "2025-01-20" }
];
const priorityOrder = { high: 0, medium: 1, low: 2 };
const sorted = _.sortBy(tasks, [
(task) => priorityOrder[task.priority],
'due'
]);
console.log(sorted.map(t => t.title));
// Output: ["Refactor", "Bug fix", "Feature"]
_.sortBy() handles null and undefined gracefully — they sort to the end by default:
const values = [3, null, 1, undefined, 5, null, 2];
const sorted = _.sortBy(values);
console.log(sorted);
// Output: [1, 2, 3, 5, null, null, undefined]
To control placement explicitly:
// Nulls first
const nullsFirst = _.sortBy(users, (u) => (u.age == null ? -Infinity : u.age));
// Nulls last
const nullsLast = _.sortBy(users, (u) => (u.age == null ? Infinity : u.age));
| Feature | _.sortBy() |
Array.sort() |
Array.toSorted() (ES2023) |
|---|---|---|---|
| Mutates original | ❌ No | ✅ Yes | ❌ No |
| Stable sort | ✅ Always | ✅ Since ES2019 | ✅ Stable |
| Sorts numbers correctly | ✅ Out of the box | ❌ Needs comparator | ❌ Needs comparator |
| Sort by property name | ✅ _.sortBy(arr, 'name') |
❌ Needs function | ❌ Needs function |
| Multiple sort keys | ✅ ['a', 'b'] |
❌ Complex comparator | ❌ Complex comparator |
| Descending order | ❌ Use _.orderBy() |
✅ Reverse comparator | ✅ Reverse comparator |
| Bundle size | ~2KB | 0KB (native) | 0KB (native) |
import orderBy from 'lodash/orderBy';
import { useState } from 'react';
function SortableTable({ data }) {
const [sortKey, setSortKey] = useState('name');
const [sortDir, setSortDir] = useState('asc');
const sortedData = orderBy(data, [sortKey], [sortDir]);
const handleSort = (key) => {
if (key === sortKey) {
setSortDir(sortDir === 'asc' ? 'desc' : 'asc');
} else {
setSortKey(key);
setSortDir('asc');
}
};
return (
<table>
<thead>
<tr>
<th onClick={() => handleSort('name')}>
Name {sortKey === 'name' && (sortDir === 'asc' ? '▲' : '▼')}
</th>
<th onClick={() => handleSort('age')}>
Age {sortKey === 'age' && (sortDir === 'asc' ? '▲' : '▼')}
</th>
</tr>
</thead>
<tbody>
{sortedData.map(row => (
<tr key={row.id}>
<td>{row.name}</td>
<td>{row.age}</td>
</tr>
))}
</tbody>
</table>
);
}
_.sortBy() uses a modified merge sort algorithm internally:
.sort()Tips:
.sort() can be faster (avoids new array allocation)// ❌ Recomputes for every comparison
const sorted = _.sortBy(largeArray, (item) => expensiveCalculation(item));
// ✅ Compute once, sort, discard keys
const sorted = _.sortBy(
largeArray.map(item => ({ item, key: expensiveCalculation(item) })),
'key'
).map(({ item }) => item);
> Tip: After sorting, you often need to group data for display. Use Lodash _.groupBy() to organize sorted results by category, and Lodash _.merge() to combine objects before sorting.
_.mergeWith(), and avoid prototype pollution._.sumBy() for aggregation._.sortBy() only sorts in ascending order. _.orderBy() accepts an additional parameter to specify sort direction — 'asc' or 'desc' — for each sort key independently. Use _.sortBy() when you always want ascending order, and _.orderBy() when you need descending or mixed sort directions.
Use _.orderBy() with 'desc' as the direction: _.orderBy(users, ['age'], ['desc']). Alternatively, reverse the result of _.sortBy(): _.sortBy(users, 'age').reverse(), or negate numeric values: _.sortBy(users, (u) => -u.age). The _.orderBy() approach is recommended.
No. _.sortBy() always returns a new sorted array. The original collection is never modified. This is different from native Array.prototype.sort(), which mutates the original array in place.
Yes. Pass the nested property path as a dot-notation string: _.sortBy(users, 'address.city'). Alternatively, pass a function: _.sortBy(users, (u) => u.address.city).
Yes. _.sortBy() guarantees a stable sort, meaning elements with equal sort values maintain their original relative order. Native Array.sort() is also stable since ES2019.
Native Array.prototype.toSorted() (ES2023) provides similar non-mutating behavior. For older environments, [...array].sort() creates a copy before sorting. However, _.sortBy() offers cleaner syntax for sorting by property names, multiple keys, and nested values.
Comments
Sign in to join the discussion.
No comments yet. Be the first to share your thoughts.