Got it — you’re asking about **`sorted(iterable, key=function)`** where instead of
writing `lambda x: ...`, we write our own **custom function** and pass it as `key`.
Let’s go step-by-step with three examples of increasing complexity:
***
## **1) Simple example**
Sort a list of strings by their length.
```python
def by_length(s):
return len(s)
words = ["apple", "kiwi", "banana", "fig"]
result = sorted(words, key=by_length)
print(result)
# Output: ['fig', 'kiwi', 'apple', 'banana']
```
📌 **What happens:**
- Python calls `by_length()` for each item.
- The `sorted()` function uses those returned values to determine the order.
***
## **2) Medium example**
Sort a list of tuples `(name, age)` by **age descending**, then by **name
alphabetically**.
```python
def sort_by_age_then_name(person):
name, age = person
return (-age, name) # negative for descending age, name for alphabetical
people = [("John", 25), ("Alice", 30), ("Bob", 30), ("Daisy", 22)]
result = sorted(people, key=sort_by_age_then_name)
print(result)
# Output: [('Alice', 30), ('Bob', 30), ('John', 25), ('Daisy', 22)]
```
📌 **What happens:**
- First, people are sorted by **age** in descending order (`-age`).
- If ages are the same, they are sorted by **name** alphabetically.
***
## **3) Complicated example**
Sort a list of dictionaries representing products by:
1. **Total price** = `price * quantity` (primary key, descending).
2. If equal, sort by **category** alphabetically.
3. If still equal, sort by **name length** ascending.
```python
def product_sort_key(product):
total_price = product['price'] * product['quantity']
return (-total_price, product['category'], len(product['name']))
products = [
{"name": "Widget", "price": 10, "quantity": 5, "category": "Tools"},
{"name": "Gadget", "price": 20, "quantity": 2, "category": "Electronics"},
{"name": "Thing", "price": 10, "quantity": 5, "category": "Electronics"},
{"name": "Device", "price": 3, "quantity": 10, "category": "Toys"}
]
result = sorted(products, key=product_sort_key)
for p in result:
print(p)
```
**Output:**
```
{'name': 'Thing', 'price': 10, 'quantity': 5, 'category': 'Electronics'}
{'name': 'Widget', 'price': 10, 'quantity': 5, 'category': 'Tools'}
{'name': 'Gadget', 'price': 20, 'quantity': 2, 'category': 'Electronics'}
{'name': 'Device', 'price': 3, 'quantity': 10, 'category': 'Toys'}
```
📌 **What happens:**
- Calculates total price for sorting (highest first).
- If tie on price, category decides.
- If tie on category, name length decides.
***
✅ This shows **simple → medium → complex** key functions.
If you like, I can also give you **the same examples in one-liner lambda form** for
quick coding in contests.
Do you want me to do that?
I see what you’re trying to do — the **first one** is fine, but the **second one**
is not syntactically correct in Python.
***
### 1 ✅ Correct:
```python
sorted(words, key=lambda x: len(x))
```
This works fine — it sorts `words` by the length of each element.
***
### 2 ❌ Wrong:
```python
sorted(people, lambda x: -x[1], x[0])
```
This is **invalid** because:
- `sorted()` only allows **one** `key` function, not multiple positional arguments
after the iterable.
- You can’t pass two values (`-x, x`) like that — the key must be a single value.
- If you need multiple sort criteria, you make the `lambda` return a **tuple**.
***
✅ **Corrected version** for your intention (“sort by `-x` then `x`”):
```python
sorted(people, key=lambda x: (-x[1], x[0]))
```
Here:
- The `key` function returns a tuple.
- Sorting in Python is lexicographic for tuples — it first sorts by the first
element (`-x`, i.e., age descending), and if there’s a tie, by the second element
(`x`, i.e., name ascending).
***
### Example:
```python
people = [("John", 25), ("Alice", 30), ("Bob", 30), ("Daisy", 22)]
result = sorted(people, key=lambda x: (-x[1], x[0]))
print(result)
# [('Alice', 30), ('Bob', 30), ('John', 25), ('Daisy', 22)]
```
***
If you like, I can **also show you the equivalent using `operator.itemgetter`** so
that it’s even cleaner.
Do you want me to show that?
Yes — your understanding is correct.
```python
result = sorted(people, key=itemgetter(1, 0), reverse=False)
```
will sort **primarily by age ascending** (`itemgetter(1)` → index `1` in each tuple
is age)
and then, if ages are equal, **by name ascending** (`itemgetter(0)` → index `0` is
name).
***
### Example
```python
from operator import itemgetter
people = [("John", 25), ("Alice", 30), ("Bob", 30), ("Daisy", 22)]
result = sorted(people, key=itemgetter(1, 0), reverse=False)
print(result)
```
**Output:**
```
[('Daisy', 22), ('John', 25), ('Alice', 30), ('Bob', 30)]
```
- Sorted first by `age` ascending: 22, 25, 30, 30
- For the two with age 30, `"Alice"` comes before `"Bob"` alphabetically.
***
📌 **Note:**
- The `reverse=False` explicitly means “ascending order” (the default).
- If you set `reverse=True` here, **both** age and name sorting would be reversed
(age descending, then name descending).
If you want **age descending but name ascending**, `itemgetter` alone isn’t enough
— you’d need the lambda trick:
```python
sorted(people, key=lambda x: (-x[1], x[0]))
```
***
Do you want me to make a small table showing all combinations of
ascending/descending on both fields? That would make it easy to remember.
Here’s a handy summary table for **sorting by two fields** in a list of tuples,
showing all combinations of ascending/descending order for both fields (let’s say
we have `people = [("John", 25), ("Alice", 30), ("Bob", 30), ("Daisy", 22)]`, where
each tuple is `(name, age)`):
| Sort Style | Python Code
| Result |
|:--------------------------|:-----------------------------------------------------
----|:------------------------------------------|
| Age ↑, Name ↑ (both ascending) | `sorted(people, key=itemgetter(1, 0))`
| [('Daisy', 22), ('John', 25), ('Alice', 30), ('Bob', 30)] |
| Age ↓, Name ↑ | `sorted(people, key=lambda x: (-x, x))` |
[('Alice', 30), ('Bob', 30), ('John', 25), ('Daisy', 22)] |
| Age ↑, Name ↓ | `sorted(people, key=lambda x: (x, -ord(x)))`* |
[('Daisy', 22), ('John', 25), ('Bob', 30), ('Alice', 30)] |
| Age ↓, Name ↓ (both descending) | `sorted(people, key=itemgetter(1, 0),
reverse=True)` | [('Bob', 30), ('Alice', 30), ('John', 25), ('Daisy', 22)] |
**Explanations:**
- `itemgetter(1, 0)`: sorts by age (ascending), then name (ascending).
- `lambda x: (-x, x)`: age descending, name ascending.
- To get **descending name**, you'd need a little trick (since strings can’t be
negated directly). A quick hack is to use `-ord(x)` for first letter, but for real
applications, use `reverse=True` on secondary sorting or sort twice (see note
below).
- `reverse=True` with `itemgetter` reverses *both* sorting criteria.
**Note:**
To sort by descending name properly (not just by the first character), you usually
do:
```python
sorted(people, key=lambda x: (x[1], x[0]), reverse=True)
```
But this will reverse age and name.
If you want to combine one ascending and one descending, always use a tuple in a
lambda!
**General rule:**
- For ascending on a field: use as-is.
- For descending on a field: use negative for numbers or reverse strings for text,
or sort items twice.
***
**Summary Example for most common:**
- **Age ascending, name ascending:**
```python
sorted(people, key=itemgetter(1, 0))
```
- **Age descending, name ascending:**
```python
sorted(people, key=lambda x: (-x[1], x[0]))
```
- **Age ascending, name descending:**
```python
sorted(people, key=lambda x: (x[1], x[0]), reverse=True)
```
*(but this reverses both; so more care needed for only name descending)*
Would you like examples for more than two sorting keys? Or examples with
dictionaries/lists instead of tuples?