All 57 lessons
Lesson 19 of 57 · Functions
Python Lambda, map, filter and Custom Sorting
By Skillancy Editorial Team
Run the code in the free Python Compiler or on your own computer. Lessons that read files use the FreshBasket practice data: download the dataset (ZIP).
Functions in Python are ordinary values. You can store one in a variable, put it in a list, and, most usefully, hand it to another function as an argument. That single idea unlocks a whole family of tools: "sort these stores by their order count", "find the product with the highest margin", "apply this cleaning rule to every value". In each case you pass a small function that describes how to look at each item.
For tiny one-off functions Python offers the lambda expression, a function written in a single line without a name. In this lesson you will learn lambdas, then use them with sorted, min, max, map and filter, and finish with a few tools from functools and operator. You will also learn when to avoid them: a readable named function almost always beats a clever lambda.
Worked examples
Example 1: Functions are values
Question: Store functions in a dictionary and call them by name.
def gst5(price):
return price * 1.05
def gst12(price):
return price * 1.12
rates = {"food": gst5, "household": gst12}
print(rates["food"](100))
print(rates["household"](100))
def apply_all(func, values):
return [func(v) for v in values]
print(apply_all(gst5, [100, 200]))Output
105.0
112.00000000000001
[105.0, 210.0]How it works: gst5 without brackets is the function object itself; gst5(100) calls it. A dictionary can hold functions as values, and apply_all receives a function as a parameter and calls it on each item. That is the whole secret behind key=.
Reading the result: A ₹100 food item costs 105.00 with tax and a household item costs 112.00. Because functions are values, choosing the rule at runtime is just a dictionary lookup.
Example 2: Your first lambda
Question: Write the same tax function as a lambda.
gst5 = lambda price: price * 1.05
print(gst5(100))
add = lambda a, b: a + b
print(add(3, 4))
print((lambda x: x ** 2)(9))
print(type(gst5).__name__)Output
105.0
7
81
functionHow it works: lambda parameters: expression creates a function whose body is a single expression, and that expression's value is returned automatically. There is no return keyword and no room for multiple statements. Lambdas are normally used inline, as in the third line, rather than being assigned to a name; if you give it a name, use def instead.
Reading the result: The lambda gave 105.0, the same as the def version, and its type is function. Lambdas are ordinary functions, only shorter to write.
Example 3: Sorting with a key function
Question: Sort a list of products by price, then by name length.
products = [("Milk", 68), ("Eggs", 96), ("Curd", 45), ("Paneer", 95), ("Bread", 45)]
by_price = sorted(products, key=lambda p: p[1])
print(by_price)
by_name_len = sorted(products, key=lambda p: len(p[0]))
print([name for name, _ in by_name_len])
print(sorted(["banana", "Apple", "cherry"]))
print(sorted(["banana", "Apple", "cherry"], key=str.lower))Output
[('Curd', 45), ('Bread', 45), ('Milk', 68), ('Paneer', 95), ('Eggs', 96)]
['Milk', 'Eggs', 'Curd', 'Bread', 'Paneer']
['Apple', 'banana', 'cherry']
['Apple', 'banana', 'cherry']How it works: sorted calls the key function on each item and sorts by the returned value, while keeping the original items in the output. The sort is stable: items with equal keys stay in their original order, which is why Curd comes before Bread in the price ordering. str.lower is an existing function, so no lambda is needed for case-insensitive sorting.
Reading the result: The cheapest item is Curd and the most expensive is Eggs. Sorting names without a key puts capitals first: ['Apple', 'banana', 'cherry'].
Example 4: Descending and multi-level sorts
Question: Rank stores by orders (highest first) and break ties alphabetically.
stores = [("Pune-1", 950), ("Delhi-1", 1200), ("Mumbai-1", 950), ("Chennai-1", 700)]
ranked = sorted(stores, key=lambda s: (-s[1], s[0]))
for name, orders_count in ranked:
print(f"{name:<10}{orders_count:>6}")
print(sorted(stores, key=lambda s: s[1], reverse=True)[0])Output
Delhi-1 1200
Mumbai-1 950
Pune-1 950
Chennai-1 700
('Delhi-1', 1200)How it works: Returning a tuple from the key sorts by the first element, then the second for ties. Negating a number (-s[1]) reverses just that part, which is how you mix ascending and descending. reverse=True reverses the whole ordering.
Reading the result: Delhi leads with 1200, and Mumbai and Pune tie at 950, so the alphabetical rule put Mumbai-1 first. Tuple keys are the standard way to do multi-column sorts, just like ORDER BY a DESC, b ASC in SQL.
Example 5: min and max with a key
Question: Find the product with the lowest price and the longest name.
cheapest = min(products, key=lambda p: p[1])
longest = max(products, key=lambda p: len(p[0]))
print(cheapest, longest)
stock = {"Milk": 120, "Curd": 80, "Paneer": 35}
print(min(stock, key=stock.get))
print(max(stock.items(), key=lambda kv: kv[1]))Output
('Curd', 45) ('Paneer', 95)
Paneer
('Milk', 120)How it works: min and max accept the same key= argument as sorted. When there are ties they return the first one found. For dictionaries, key=stock.get compares the keys by their values, as we saw in the dictionaries lesson.
Reading the result: The cheapest product is Curd at ₹45, the product with the longest name is Paneer, and the scarcest stock item is Paneer.
Example 6: map
Question: Apply a function to every item without writing a loop.
prices = [68, 45, 96, 62]
with_tax = map(lambda p: round(p * 1.05, 2), prices)
print(with_tax)
print(list(with_tax))
print(list(map(str, prices)))
print(list(map(len, ["milk", "curd", "paneer"])))
print(list(map(lambda a, b: a * b, [2, 3], [10, 20])))Output
<map object at 0x7fa351641270>
[71.4, 47.25, 100.8, 65.1]
['68', '45', '96', '62']
[4, 4, 6]
[20, 60]How it works: map(func, iterable) returns a lazy iterator that applies func to each element; wrap it in list to see the values. With two iterables, the function receives two arguments at a time. A map with an existing function like str or len is neat; with a lambda, a list comprehension is usually clearer.
Reading the result: The tax-inclusive prices are [71.4, 47.25, 100.8, 65.1]. Notice that printing a map object directly shows only its type, because nothing is computed until you consume it.
Example 7: filter
Question: Keep only the items that satisfy a rule.
minutes = [12, 35, 18, 41, 9, 27, 0]
fast = list(filter(lambda m: m <= 20, minutes))
print(fast)
nonzero = list(filter(None, minutes))
print(nonzero)
words = ["milk", "", "curd", " ", "eggs"]
print(list(filter(str.strip, words)))Output
[12, 18, 9, 0]
[12, 35, 18, 41, 9, 27]
['milk', 'curd', 'eggs']How it works: filter(func, iterable) keeps the items for which func returns a truthy value. Passing None as the function keeps all truthy items, dropping zeros and empty strings. str.strip returns an empty (false) string for blanks, so it works as a ready-made test.
Reading the result: 4 of 7 deliveries were fast, and filtering with None removed the zero, leaving 6 values. The comprehension equivalent, [m for m in minutes if m <= 20], gives the same list.
Example 8: operator.itemgetter and attrgetter
Question: Sort records without writing lambdas.
from operator import itemgetter
records = [
{"order_id": "O1", "city": "Pune", "amount": 450},
{"order_id": "O2", "city": "Delhi", "amount": 1200},
{"order_id": "O3", "city": "Pune", "amount": 300},
]
by_amount = sorted(records, key=itemgetter("amount"), reverse=True)
print([r["order_id"] for r in by_amount])
by_city_amount = sorted(records, key=itemgetter("city", "amount"))
print([r["order_id"] for r in by_city_amount])Output
['O2', 'O1', 'O3']
['O2', 'O3', 'O1']How it works: itemgetter("amount") builds a function that fetches ["amount"] from whatever it is given. With two names it returns a tuple, giving a two-level sort. It is faster than a lambda and reads like a column name list. This is the plain-Python equivalent of df.sort_values(["city", "amount"]).
Reading the result: By amount, the order is ['O2', 'O1', 'O3']; by city then amount it is ['O2', 'O3', 'O1'].
Example 9: reduce
Question: Combine a list into a single value step by step.
from functools import reduce
nums = [3, 5, 2, 8]
product = reduce(lambda acc, x: acc * x, nums)
print(product)
running = reduce(lambda acc, x: acc + [acc[-1] + x], nums[1:], [nums[0]])
print(running)
print(sum(nums), max(nums))Output
240
[3, 8, 10, 18]
18 8How it works: reduce(func, items) feeds the first two items to func, then feeds that result with the third item, and so on. The third argument is an optional starting value. For sums and maxima the built-ins are better; reduce is useful for custom combinations, though many programmers prefer an explicit loop for clarity.
Reading the result: The product of the numbers is 240 and the running totals are [3, 8, 10, 18]. The itertools function accumulate does the running total more simply.
Example 10: Real data, ranking cities
Question: Rank the cities by their number of delivered orders using sorting tools on real data.
import pandas as pd
from collections import Counter
orders = pd.read_csv("orders.csv")
stores = pd.read_csv("stores.csv")
print(list(stores.columns))
city_of = dict(zip(stores["store_id"], stores["city"]))
delivered = orders[orders["order_status"] == "Delivered"]
counts = Counter(city_of[s] for s in delivered["store_id"])
ranked = sorted(counts.items(), key=lambda kv: (-kv[1], kv[0]))
for city, n in ranked:
print(f"{city:<12}{n:>6,}")Output
['store_id', 'city', 'store_name', 'manager', 'opened_on']
Delhi 1,159
Bengaluru 1,156
Mumbai 869
Hyderabad 616
Pune 467
Chennai 414
Kolkata 358
Ahmedabad 352How it works: We map each store id to its city with a dictionary, count delivered orders per city with Counter, and sort the (city, count) pairs by descending count then name. Every tool in this lesson appears: a mapping, a generator expression, a key function and a tuple key.
Reading the result: Delhi leads with 1,159 delivered orders, and Ahmedabad has the fewest with 352. In pandas this entire block shrinks to a merge, a groupby and a sort_values, which you will write in the analytics modules.
Common mistakes
1. Writing a long lambda. If it needs more than one simple expression, use def.
2. Assigning a lambda to a name. f = lambda x: x + 1 gains nothing over def f(x): return x + 1 and gives worse error messages.
3. Forgetting that map and filter are lazy.
m = map(abs, [-1, -2])
print(list(m))
print(list(m))Output
[1, 2]
[]The second print is empty because the iterator was already consumed.
4. Sorting mixed types.
sorted([3, "a", 1])Output
Traceback (most recent call last):
File "example.py", line 1, in <module>
sorted([3, "a", 1])
~~~~~~^^^^^^^^^^^^^
TypeError: '<' not supported between instances of 'str' and 'int'5. Expecting sort() to return the list. list.sort() sorts in place and returns None; sorted() returns a new list.
Practice questions
Practice 1 (Easy): Sort by last letter
Sort ["milk", "eggs", "curd", "bread"] by the last letter of each word.
Show solution
words = ["milk", "eggs", "curd", "bread"]
print(sorted(words, key=lambda w: w[-1]))Output
['curd', 'bread', 'milk', 'eggs']The key returns the last character; ties keep their original order because sorting is stable. The result is ['curd', 'bread', 'milk', 'eggs'].
Practice 2 (Medium): Top two by value
From {"Pune": 950, "Delhi": 1200, "Mumbai": 1100, "Chennai": 700}, print the two cities with the highest values using sorted and a slice.
Show solution
sales = {"Pune": 950, "Delhi": 1200, "Mumbai": 1100, "Chennai": 700}
top2 = sorted(sales, key=sales.get, reverse=True)[:2]
print(top2)Output
['Delhi', 'Mumbai']Sorting the keys by their values and slicing the first two gives ['Delhi', 'Mumbai']. heapq.nlargest(2, sales, key=sales.get) does the same more efficiently on big inputs.
Practice 3 (Hard): Multi-level ranking on real data
Using customers.csv, list the five customers with the lowest customer ids among Prime members, then explain how you would sort customers by is_prime descending and customer_id ascending.
Show solution
customers = pd.read_csv("customers.csv")
rows = customers.to_dict("records")
prime = [r for r in rows if r["is_prime"] in (1, True, "True", "Yes")]
first5 = sorted(prime, key=lambda r: r["customer_id"])[:5]
print([r["customer_id"] for r in first5])
mixed = sorted(rows, key=lambda r: (not bool(r["is_prime"]), r["customer_id"]))
print(mixed[0]["customer_id"], mixed[-1]["customer_id"])Output
['C0001', 'C0009', 'C0010', 'C0014', 'C0016']
C0001 C0600Boolean not reverses the order of is_prime so Prime members come first, and the second tuple element orders by id. There are 161 Prime customers out of 600. Check the column values first if your file stores Prime as 0/1 or Yes/No.
Key takeaways
- Functions are values: you can store them, pass them in and return them.
lambda args: expressionis a one-line anonymous function; use it inline, not as a named function.sorted,minandmaxtakekey=; return a tuple from the key to sort by several fields, and negate numbers to reverse part of the ordering.mapandfilterare lazy; wrap them inlistto see results, and prefer comprehensions when they read better.itemgetteris a clean, fast replacement for simple lambdas on dictionaries and tuples.- Sorting is stable, so sorting in stages works.
Frequently asked questions
When should I use a lambda instead of def?
Only for short, throwaway functions passed straight into another call, such as a key= argument. Anything you reuse or that is hard to read at a glance deserves a name.
Are map and filter better than comprehensions?
Most Python style guides prefer comprehensions for readability. map and filter shine when you already have a named function, such as map(str, numbers).
What does stable sorting mean?
Items with equal keys keep their original relative order. This lets you sort in stages: sort by the secondary key first, then by the primary key.
Why does sorted return a list even for a dictionary or a set?
sorted accepts any iterable and always returns a new list. For a dictionary, it sorts the keys unless you pass .items().
Can a lambda have default arguments or *args?
Yes: lambda x, y=2: x * y and lambda *a: sum(a) both work, but if you need that much, def is clearer.
How is this related to pandas?
Pandas has sort_values, apply and nlargest, which replace most of these tools on tables. The key-function idea carries over directly, for example df["city"].apply(str.title).
Keep learning Python
One email a week with Python and analytics guides, new practice questions and cohort dates. No spam, unsubscribe anytime.
Ready to practise? Try the Python interview questions