Intermediate: core algorithms · Sorting and searching · topic 28 of 61 · 35 min
Sorting in real code: keys, comparators, counting sort
Explanation
sorted() versus list.sort()
sorted(x) works on any iterable and returns a NEW list; the original is untouched. lst.sort() sorts the list in place and returns None, so lst = lst.sort() leaves you holding None. Both take the same two optional arguments: key and reverse. Both are O(n log n) in the worst case.
Key functions
A key function turns each item into the value to compare. It is called once per item, then the sort compares the keys. key=len sorts strings by length; key=str.lower ignores case; key=lambda s: s[1] sorts tuples by their second field. reverse=True sorts from largest to smallest and still keeps equal items in their original order.
words = ["kiwi", "Banana", "apple", "fig"]
print(sorted(words)) # Output: ['Banana', 'apple', 'fig', 'kiwi']
print(sorted(words, key=str.lower)) # Output: ['apple', 'Banana', 'fig', 'kiwi']
print(sorted(words, key=len)) # Output: ['fig', 'kiwi', 'apple', 'Banana']Sorting by several fields
Return a tuple from the key: Python compares tuples field by field, left to right. For "marks high to low, then name A to Z", negate the number: key=lambda s: (-s["marks"], s["name"]). Negation works only for numbers. For descending strings, use stability instead: sort by the second field first, then by the first field; the second sort keeps the order of the first among ties.
students = [("Ravi", 75), ("Asha", 82), ("Kiran", 75), ("Meena", 82)]
print(sorted(students, key=lambda s: (-s[1], s[0])))
# Output: [('Asha', 82), ('Meena', 82), ('Kiran', 75), ('Ravi', 75)]
# Same result with two stable sorts: secondary key first, then primary
by_name = sorted(students, key=lambda s: s[0])
print(sorted(by_name, key=lambda s: s[1], reverse=True))
# Output: [('Asha', 82), ('Meena', 82), ('Kiran', 75), ('Ravi', 75)]Timsort: what Python uses
Python's sort is Timsort, a hybrid of merge sort and insertion sort designed by Tim Peters for Python. It finds runs that are already sorted, extends short runs with insertion sort, and merges runs. So it is O(n log n) in the worst case, O(n) when the input is already sorted, stable, and may use up to O(n) extra memory. V8, the engine in Chrome and Node.js, also uses Timsort for Array.prototype.sort.
Custom comparisons: cmp_to_key
Sometimes the order is defined by comparing two items, not by a key per item. Classic example: arrange numbers to form the largest number. 9 should come before 34, and 3 before 30, because "330" > "303". The rule is: a goes first if a + b > b + a as strings. A comparator returns a negative number if a should come first, positive if b should, and 0 for a tie. functools.cmp_to_key turns it into a key.
from functools import cmp_to_key
def compare(a, b):
if a + b > b + a:
return -1 # a first
if a + b < b + a:
return 1 # b first
return 0
nums = [3, 30, 34, 5, 9]
parts = sorted(map(str, nums), key=cmp_to_key(compare))
print("".join(parts)) # Output: 9534330Counting sort: O(n + k) for small ranges
When the values are integers in a small range 0..k, like exam marks 0 to 100 or ages, you do not need comparisons at all. Count how many times each value appears in an array of size k + 1, then write each value out count times. That is O(n + k) time and O(k) extra space, faster than O(n log n) when k is not much bigger than n. It is useless when the range is huge (phone numbers), because the count array would be enormous.
marks = [72, 95, 72, 40, 100, 95]
count = [0] * 101
for m in marks:
count[m] += 1
result = [v for v in range(101) for _ in range(count[v])]
print(result) # Output: [40, 72, 72, 95, 95, 100]JavaScript sort pitfalls
Without a comparator, Array.prototype.sort converts items to strings and compares them as text, so [10, 9, 1].sort() gives [1, 10, 9], because "10" comes before "9" as text. Always pass a comparator for numbers: (a, b) => a - b for ascending. A comparator must return a number (negative, zero or positive); returning a boolean such as a > b gives wrong results in some engines. sort also changes the array in place; copy first with [...arr].sort(…) or use arr.toSorted(…) (ES2023). Since ES2019, sort is required to be stable.
console.log([10, 9, 1].sort()); // Output: [ 1, 10, 9 ]
console.log([10, 9, 1].sort((a, b) => a - b)); // Output: [ 1, 9, 10 ]
const people = [{ n: "Ira", age: 30 }, { n: "Om", age: 25 }];
console.log([...people].sort((a, b) => a.age - b.age).map((p) => p.n)); // Output: [ 'Om', 'Ira' ]Common mistakes
- Writing lst = lst.sort(), which sets lst to None. Fix: use lst.sort() alone, or new = sorted(lst).
- Negating a string in a tuple key (-name raises TypeError). Fix: two stable sorts, or cmp_to_key.
- Calling .sort() with no comparator on numbers in JavaScript. Fix: (a, b) => a - b.
- Using counting sort when the range is huge or the values are not integers. Fix: use it only for small integer ranges.
- Forgetting the "all zeros" case in largest number: [0, 0] should give "0", not "00". Fix: if the first part is "0", return "0".
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Sorting in real code: keys, comparators, counting sort · 1/8
This lesson is about sorting in real code: telling the built-in sort what to compare.
Prefer a video by a teacher? Find free videos on “python sorted key lambda multiple keys cmp_to_key counting sort javascript sort comparator” on YouTube ↗
In real life: Ranking candidates in a placement drive
A college placement cell shortlists students by CGPA from high to low; ties go to the student with fewer backlogs, and then alphabetical order. That is one tuple key. Marks out of 100 for a whole university can be sorted with counting sort in one pass, because the range is tiny compared with the number of students.
students = [
{"name": "Priya", "cgpa": 8.7, "backlogs": 0},
{"name": "Aman", "cgpa": 9.1, "backlogs": 1},
{"name": "Dev", "cgpa": 8.7, "backlogs": 0},
{"name": "Zoya", "cgpa": 8.7, "backlogs": 2},
]
ranked = sorted(students, key=lambda s: (-s["cgpa"], s["backlogs"], s["name"]))
print([s["name"] for s in ranked])Practice (0/4 done)
students is a list of (name, marks) tuples. Write rank_students(students) that returns a NEW list sorted by marks from HIGH to LOW, and for equal marks by name from A to Z. Use sorted() with a tuple key. Example: rank_students([("Ravi", 75), ("Asha", 82), ("Kiran", 75)]) returns [("Asha", 82), ("Kiran", 75), ("Ravi", 75)].
Write counting_sort(nums, k) for integers that are all in the range 0 to k (inclusive). Return a NEW sorted list in O(n + k) time using a count list of size k + 1. Do not call sorted() or .sort(). Example: counting_sort([3, 1, 2, 3, 0], 3) returns [0, 1, 2, 3, 3].
Write largest_number(nums) for a NON-EMPTY list of non-negative integers. Arrange them so that, written one after another, they form the largest possible number, and return it as a STRING. Use functools.cmp_to_key with the rule "a goes first if a + b > b + a" (as strings). If the result starts with "0", return "0" (so [0, 0] gives "0", not "00"). Example: largest_number([3, 30, 34, 5, 9]) returns "9534330".
Write sortPeople(people) in JavaScript. Each person is an object { name, city, age }. Return a NEW array (do not change the input) sorted by city from A to Z (plain string comparison with < and >; all city names start with a capital letter), and for the same city by age from YOUNGEST to oldest, comparing ages as numbers. People with the same city and age keep their original order. Example: [{name: "Ira", city: "Pune", age: 30}, {name: "Om", city: "Delhi", age: 41}] becomes Om first, then Ira.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Binary searchSorting and searching · same module
- Sorting with keyPython Programming · Working with data
- Sorting with Comparable and ComparatorJava Programming (Core to Spring Boot) · The collections framework
- STL algorithms: sort, find, count, accumulate and binary searchC++ Programming (modern C++ and DSA) · The Standard Template Library (STL)
- Recursion and sorting algorithmsPython Programming · Algorithms, data structures and testing
- List methods and slicingPython Programming · Lists, tuples, dictionaries and sets
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What does nums = nums.sort() leave in nums?
Answer: None
list.sort() sorts in place and returns None, so the name is rebound to None.
Which key sorts (name, marks) tuples by marks high to low, then name A to Z?
Answer: lambda s: (-s[1], s[0])
The tuple compares marks first (negated for high to low), then the name.
What does [10, 9, 1].sort() return in JavaScript?
Answer: [1, 10, 9]
Without a comparator, items are compared as strings, and "10" comes before "9".
When is counting sort a good choice?
Answer: Integers in a small range, such as marks 0 to 100
It needs a count slot for every possible value, so the range k must be small.
Why does sorting by name first and then (stably) by marks give "marks, then name"?
Answer: Because the second sort is stable, so students with equal marks stay in name order
A stable sort keeps the earlier order among ties, so the name order survives within each mark.
Which key sorts (name, marks) pairs by marks high to low, then name A to Z?Related
Answer: lambda s: (-s[1], s[0])
Negative marks puts high marks first, and the name breaks ties in A to Z order.
From Sorting with key
Is List.sort stable?Related
Answer: Yes, equal elements keep their order
Java's object sort is stable.
Insertion sort on a list that is already sorted takes…Related
Answer: O(n)
Each key is compared once with its left neighbour and nothing moves.
Why is [](int a, int b) { return a <= b; } a bad comparator?Related
Answer: It is not a strict weak ordering, so std::sort may misbehave or crash
For equal elements it says each comes before the other, which breaks sort's assumptions.
From STL algorithms: sort, find, count, accumulate and binary search
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 2 more topics in “Sorting and searching” before its exam: Binary search, Binary search on the answer.
- Module 6 exam: Sorting and searching · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Trees and binary search trees. 61 topics left in the course before the final exam.