Skip to content

itertools & functools

itertools: lazy iteration without dependencies

Section titled “itertools: lazy iteration without dependencies”

TypeScript developers often reach for Lodash for collection utilities. Python ships itertools in the standard library, and it is lazy — it produces values on demand without building intermediate lists. This matters for large datasets.

TypeScript
// TypeScript / Lodash
import _ from 'lodash';
const result = _.concat([1, 2], [3, 4], [5]);
// or spread:
const result2 = [...[1, 2], ...[3, 4], ...[5]];
console.log(result); // [1, 2, 3, 4, 5]
Python
import itertools
result = list(itertools.chain([1, 2], [3, 4], [5]))
print(result) # [1, 2, 3, 4, 5]
# chain.from_iterable — flatten one level of nesting
nested = [[1, 2], [3, 4], [5]]
flat = list(itertools.chain.from_iterable(nested))
print(flat) # [1, 2, 3, 4, 5]
TypeScript
// TypeScript — manual counter or generator
function* count(start = 0, step = 1) {
let n = start;
while (true) {
yield n;
n += step;
}
}
const gen = count(10, 2);
const result = Array.from({ length: 4 }, () => gen.next().value);
console.log(result); // [10, 12, 14, 16]
Python
import itertools
counter = itertools.count(10, 2) # infinite lazy counter
result = [next(counter) for _ in range(4)]
print(result) # [10, 12, 14, 16]
# islice — safely take N items from an infinite iterator
first_five = list(itertools.islice(itertools.count(1), 5))
print(first_five) # [1, 2, 3, 4, 5]

groupby is like SQL GROUP BY but it requires sorted input — it groups consecutive equal keys.

TypeScript
// TypeScript / Lodash
import _ from 'lodash';
const data = [
{ cat: "a", val: 1 }, { cat: "a", val: 2 },
{ cat: "b", val: 3 }, { cat: "b", val: 4 },
];
const grouped = _.groupBy(data, "cat");
// { a: [...], b: [...] }
Python
import itertools
data = [("a", 1), ("a", 2), ("b", 3), ("b", 4)]
# Input must be sorted by key first!
for key, group in itertools.groupby(data, key=lambda x: x[0]):
items = list(group)
print(f"{key}: {items}")
# a: [('a', 1), ('a', 2)]
# b: [('b', 3), ('b', 4)]
TypeScript
// TypeScript
const sum = [1, 2, 3, 4, 5].reduce((acc, x) => acc + x, 0);
console.log(sum); // 15
Python
import functools
result = functools.reduce(lambda acc, x: acc + x, [1, 2, 3, 4, 5])
print(result) # 15
# With initial value
result2 = functools.reduce(lambda acc, x: acc + x, [1, 2, 3], 100)
print(result2) # 106

@lru_cache is Python’s built-in memoization decorator. The equivalent in TypeScript is a manual Map-based cache or a library like memoize-one.

TypeScript
// TypeScript — manual memoization
const memo = new Map<number, number>();
function fib(n: number): number {
if (n < 2) return n;
if (memo.has(n)) return memo.get(n)!;
const result = fib(n - 1) + fib(n - 2);
memo.set(n, result);
return result;
}
console.log(fib(30)); // 832040
Python
import functools
@functools.lru_cache(maxsize=128)
def fib(n: int) -> int:
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
print(fib(30)) # 832040
print(fib.cache_info()) # hits=28, misses=31, ...
TypeScript
// TypeScript — bind or arrow wrapper
function multiply(a: number, b: number): number {
return a * b;
}
const double = multiply.bind(null, 2);
// or: const double = (b: number) => multiply(2, b);
console.log(double(5)); // 10
console.log(double(10)); // 20
Python
import functools
def multiply(a: int, b: int) -> int:
return a * b
double = functools.partial(multiply, 2)
print(double(5)) # 10
print(double(10)) # 20
# partial also works with keyword arguments
def connect(host: str, port: int, ssl: bool = False) -> str:
return f"{'https' if ssl else 'http'}://{host}:{port}"
prod = functools.partial(connect, port=443, ssl=True)
print(prod("api.example.com")) # https://api.example.com:443
import itertools
import functools
# chain
merged = list(itertools.chain([1, 2], [3, 4], [5]))
print("chain:", merged)
# islice from count
first_six = list(itertools.islice(itertools.count(0, 10), 6))
print("count+islice:", first_six)
# groupby
words = ["apple", "ant", "bear", "banana", "cat"]
words.sort(key=lambda w: w[0]) # must sort first!
for letter, group in itertools.groupby(words, key=lambda w: w[0]):
print(f" {letter}: {list(group)}")
# lru_cache fibonacci
@functools.lru_cache(maxsize=64)
def fib(n: int) -> int:
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
print("fib(20):", fib(20))
print("cache:", fib.cache_info())
# partial
def power(base: int, exp: int) -> int:
return base ** exp
square = functools.partial(power, exp=2)
cube = functools.partial(power, exp=3)
print("squares:", [square(x) for x in range(1, 6)])
print("cubes :", [cube(x) for x in range(1, 6)])
Why does `itertools.groupby` require sorted input?
What does `@functools.lru_cache` do?
What does `functools.partial(fn, 2)` return?
Which itertools function safely takes N items from an infinite iterator?