Skip to content

Python · TypeScript · C# toolkit

In an interview you shouldn’t have to think about which call gives you a heap or how division rounds. This page puts the interview-relevant standard library of the three languages side by side, and flags the traps (⚠) that quietly produce wrong answers.

Baseline versions: Python 3.12+, TypeScript on Node 22 (ES2024 library), C# 12 on .NET 8+. Anything newer is marked (Python 3.14 heapq max-heap functions, .NET 9 CountBy and Index). Every snippet below runs and checks itself.

Filter by area, search for a word (“round”, “tuple”, “stable”), hide languages you don’t use, or show only the traps.

Stdlib side by side
67 of 67 rows
languages
show
operation
Python
TypeScript
C#
n zeros
Arrays
[0] * n
new Array<number>(n).fill(0)
new int[n]  // zero-filled
new List<int>(capacity: n)  // empty
⚠2-D grid
Arrays
[[0] * c for _ in range(r)]
Array.from({ length: r }, () =>
  new Array<number>(c).fill(0))
new int[r, c]  // rectangular
new int[r][]  // jagged, fill rows
Python [[0] * c] * r and JS Array(r).fill([]) repeat ONE row object r times: writing grid[0][0] changes every row.
push / pop at the end, O(1)
Arrays
xs.append(x)
xs.pop()
xs.push(x)
xs.pop()
list.Add(x)
list.RemoveAt(list.Count - 1)
last element
Arrays
xs[-1]
xs.at(-1)  // or xs[xs.length - 1]
xs[^1]
slice (copy)
Arrays
xs[i:j]
xs.slice(i, j)
arr[i..j]  // arrays: a copy
list.GetRange(i, j - i)
insert / delete in the middle, O(n)
Arrays
xs.insert(i, x)
del xs[i]
xs.splice(i, 0, x)
xs.splice(i, 1)
list.Insert(i, x)
list.RemoveAt(i)
find
Arrays
x in xs
xs.index(x)  # ValueError if absent
xs.includes(x)
xs.indexOf(x)  // -1 if absent
xs.Contains(x)
list.IndexOf(x)  // -1 if absent
reverse
Arrays
xs.reverse()  # in place
xs[::-1]  # copy
xs.reverse()  // in place
xs.toReversed() // copy (ES2023)
Array.Reverse(arr)  // in place
list.Reverse()  // in place
copy
Arrays
xs.copy()  # shallow
copy.deepcopy(xs)
[...xs]  // shallow
structuredClone(xs)
[.. xs]  // shallow
(int[])arr.Clone()
create
Maps
d: dict[str, int] = {}
const m = new Map<string, number>()
var d = new Dictionary<string, int>();
read with a default
Maps
d.get(k, 0)
m.get(k) ?? 0
d.GetValueOrDefault(k)
d.TryGetValue(k, out var v)
⚠read a missing key
Maps
d[k]  # KeyError
m.get(k)  // undefined
d[k]  // KeyNotFoundException
Three different failure modes. Python and C# throw; TS returns undefined, which then turns arithmetic into NaN.
write / has / delete
Maps
d[k] = v
k in d
d.pop(k, None)
m.set(k, v)
m.has(k)
m.delete(k)
d[k] = v  // Add(k, v) throws if present
d.ContainsKey(k)
d.Remove(k)
iterate
Maps
for k, v in d.items():
for (const [k, v] of m) {}
foreach (var (k, v) in d) {}
⚠iteration order
Maps
insertion order (guaranteed since 3.7)
insertion order (guaranteed)
unspecified: do not rely on it
SortedDictionary for key order
size
Maps
len(d)
m.size
d.Count
⚠plain object as a map
Maps
—
avoid {}: keys become strings,
and "constructor" etc. already exist
—
create / add / remove / has
Sets
s = set()
s.add(x); s.discard(x)
x in s
const s = new Set<number>()
s.add(x); s.delete(x)
s.has(x)
var s = new HashSet<int>();
s.Add(x); s.Remove(x);
s.Contains(x)
C# Add returns false if the item was already there: a free "seen before?" test.
union / intersection / difference
Sets
a | b, a & b, a - b
a.union(b), a.intersection(b),
a.difference(b)  // Node 22+
a.UnionWith(b)  // mutates a!
a.IntersectWith(b); a.ExceptWith(b)
a.Union(b)  // LINQ, new sequence
⚠FIFO queue
Queues & stacks
q = deque()
q.append(x); q.popleft()
no queue: use a head index
(q.shift() is O(n))
var q = new Queue<int>();
q.Enqueue(x); q.Dequeue()
Python list.pop(0) and JS shift() are O(n) per call, so a BFS built on them is O(n²).
deque (both ends)
Queues & stacks
q.appendleft(x); q.pop()
deque(maxlen=k)  # bounded
none: ring buffer or two stacks
none: LinkedList<T> with
AddFirst / RemoveLast
stack
Queues & stacks
st.append(x); st.pop(); st[-1]
st.push(x); st.pop(); st.at(-1)
var st = new Stack<int>();
st.Push(x); st.Pop(); st.Peek()
⚠min-heap push / pop / peek
Heaps
heapq.heappush(h, x)
heapq.heappop(h)
h[0]
none built in:
write a ~30-line binary heap
var pq = new PriorityQueue<string, int>();
pq.Enqueue(item, prio); pq.Dequeue()
pq.Peek()
build from a list, O(n)
Heaps
heapq.heapify(xs)  # in place
(your heap)
new PriorityQueue<E, P>(pairs)
max-heap
Heaps
push -x, negate on pop
# 3.14+: heapq.heappush_max(h, x)
flip the comparator
new PriorityQueue<int, int>(
  Comparer<int>.Create((a, b) => b.CompareTo(a)))
ties: FIFO among equals
Heaps
push (prio, seq, item)
compare (prio, seq)
priority (prio, seq) as a value tuple
heapq compares whole tuples, so a seq counter also stops it from comparing the items themselves. PriorityQueue is not stable.
top k
Heaps
heapq.nlargest(k, xs, key=f)
sort + slice, or a size-k heap
xs.OrderByDescending(f).Take(k)
change a priority
Heaps
no decrease-key: push again,
skip stale entries on pop
same lazy-deletion trick
same trick; .NET 9 Remove()
is a linear scan
⚠ordered set / map
Sorted
none in stdlib (sortedcontainers is
third-party: ask before using)
none: sorted array + binary search
SortedSet<T>, SortedDictionary<K, V>
// red-black trees, O(log n)
binary search
Sorted
bisect.bisect_left(xs, x)
bisect.bisect_right(xs, x)
write lowerBound(xs, x)
Array.BinarySearch(arr, x)
// returns ~insertionPoint if absent
insert keeping order
Sorted
bisect.insort(xs, x)  # O(n) shift
xs.splice(lowerBound(xs, x), 0, x)
set.Add(x)  // O(log n)
floor: largest ≤ x
Sorted
i = bisect_right(xs, x) - 1
# i == -1 means none
upperBound(xs, x) - 1
set.GetViewBetween(int.MinValue, x).Max
// 0 (default) if the view is empty
min / max
Sorted
xs[0], xs[-1]
xs[0], xs.at(-1)
set.Min, set.Max
⚠sort numbers
Sorting
xs.sort()
sorted(xs)  # copy
xs.sort((a, b) => a - b)
xs.toSorted((a, b) => a - b)
Array.Sort(arr); list.Sort();
xs.Order()  // LINQ, .NET 7+
JS sort() with no comparator compares as strings: [10, 9, 1].sort() gives [1, 10, 9].
⚠stable?
Sorting
yes (Timsort)
yes (required since ES2019)
Array.Sort / List.Sort: NO (introsort)
LINQ OrderBy: yes
by key
Sorting
sorted(ps, key=lambda p: p.age)
ps.sort((a, b) => a.age - b.age)
ps.OrderBy(p => p.Age)
list.Sort((a, b) => a.Age.CompareTo(b.Age))
desc by score, then name
Sorting
key=lambda p: (-p.score, p.name)
(a, b) => b.score - a.score ||
  (a.name < b.name ? -1 : a.name > b.name ? 1 : 0)
ps.OrderByDescending(p => p.Score)
  .ThenBy(p => p.Name, StringComparer.Ordinal)
custom comparator
Sorting
sorted(xs, key=functools.cmp_to_key(cmp))
xs.sort(cmp)
list.Sort(cmp)
Comparer<T>.Create(cmp)
⚠comparing strings
Sorting
a < b  # code points
a < b  // UTF-16 units
localeCompare: locale-aware
string.CompareOrdinal(a, b)
// default compare may be culture-aware
⚠comparator overflow
Sorting
n/a (key functions)
a - b fine for normal numbers
a - b can overflow int:
use a.CompareTo(b)
build in a loop
Strings
"".join(parts)
parts.join("")  // += is also OK in V8
var sb = new StringBuilder();
sb.Append(x); sb.ToString()
Strings are immutable in all three. Repeated s += x copies in Python and C# can turn a loop O(n²).
char ↔ code
Strings
ord(c), chr(n)
s.charCodeAt(i), s.codePointAt(i)
String.fromCharCode(n)
(int)c, (char)n
c - 'a'  // letter index
⚠length of "👍"
Strings
len(s) == 1  # code points
s.length === 2  // UTF-16 units
[...s].length === 1
s.Length == 2  // UTF-16 units
new StringInfo(s).LengthInTextElements
mutable copy
Strings
cs = list(s); "".join(cs)
const cs = [...s]; cs.join("")
var cs = s.ToCharArray(); new string(cs)
split on whitespace
Strings
s.split()  # drops empties
s.split(" ")  # keeps them
s.trim().split(/\s+/)
s.Split(' ', StringSplitOptions.RemoveEmptyEntries)
char tests
Strings
c.isdigit(), c.isalpha(), c.lower()
/[0-9]/.test(c), c.toLowerCase()
char.IsDigit(c), char.IsLetter(c)
char.ToLowerInvariant(c)
reverse
Strings
s[::-1]
[...s].reverse().join("")
string.Concat(s.Reverse())
⚠integer limits
Numbers
unbounded int
Number.MAX_SAFE_INTEGER (2⁵³−1)
bigint for more: 10n ** 30n
int.MaxValue (2³¹−1), long.MaxValue
wraps silently unless checked
⚠integer division of -7 by 2
Numbers
-7 // 2 == -4  # floor
-7 % 2 == 1
Math.trunc(-7 / 2) === -3
-7 % 2 === -1
-7 / 2 == -3  // truncates
-7 % 2 == -1
Python floors and % follows the divisor. JS and C# truncate toward zero and % follows the dividend. Safe modulo: ((a % m) + m) % m.
"infinity" sentinel
Numbers
math.inf
Infinity
int.MaxValue (careful: +1 wraps)
double.PositiveInfinity
⚠round 2.5
Numbers
round(2.5) == 2  # half to even
Math.round(2.5) === 3
Math.round(-2.5) === -2
Math.Round(2.5) == 2  // to even
MidpointRounding.AwayFromZero
exact decimals
Numbers
Decimal("0.1"), Fraction(1, 3)
integer cents, or bigint
decimal: 0.1m + 0.2m == 0.3m
parse an int
Numbers
int("42")  # ValueError if bad
Number("42")  // NaN if bad
parseInt("42px") === 42 (!)
int.Parse("42")  // throws
int.TryParse(s, out var n)
bits
Numbers
x.bit_count(), x >> 1, x & 1
x >> 1, x & 1  // 32-bit only!
x >>> 0 for unsigned
int.PopCount(x)  // .NET 7+
x >> 1, x & 1
count occurrences
Counting
Counter(xs)
cnt.most_common(k)
m.set(x, (m.get(x) ?? 0) + 1)
d[x] = d.GetValueOrDefault(x) + 1;
xs.CountBy(x => x)  // .NET 9+
group into lists
Counting
g = defaultdict(list)
g[k].append(x)
Map.groupBy(xs, f)  // ES2024, Node 21+
xs.GroupBy(f) / xs.ToLookup(f)
get-or-create
Counting
d.setdefault(k, []).append(x)
let a = m.get(k);
if (!a) m.set(k, (a = []));
a.push(x);
if (!d.TryGetValue(k, out var a))
  d[k] = a = [];
a.Add(x);
⚠pair as a key
Keys
seen.add((r, c))  # tuples hash
seen.add(`${r},${c}`)
// or r * COLS + c
new HashSet<(int, int)>()
// value tuples hash by value
JS arrays and objects are compared by reference: new Set([[1, 2]]).has([1, 2]) is false. C# int[] has the same problem.
list as a key
Keys
TypeError: unhashable;
use tuple(xs) or frozenset(xs)
xs.join(",")
string.Join(",", xs), or a record
with value equality
index + value
Iteration
for i, x in enumerate(xs):
for (const [i, x] of xs.entries())
foreach (var (i, x) in xs.Index())  // .NET 9+
xs.Select((x, i) => ...)
zip
Iteration
zip(a, b, strict=True)
a.map((x, i) => [x, b[i]])
a.Zip(b)
range / countdown
Iteration
range(n), range(n - 1, -1, -1)
for (let i = n - 1; i >= 0; i--)
Enumerable.Range(0, n)
for (int i = n - 1; i >= 0; i--)
adjacent pairs
Iteration
itertools.pairwise(xs)
for (let i = 1; i < xs.length; i++)
xs.Zip(xs.Skip(1))
combinations / permutations
Iteration
itertools.combinations(xs, k)
itertools.permutations(xs)
itertools.product(a, b)
none: write the recursion
none: write the recursion
running totals / chunks
Iteration
itertools.accumulate(xs)
itertools.batched(xs, n)  # 3.12+
manual loop
manual loop
xs.Chunk(n)  // .NET 6+
any / all
Iteration
any(f(x) for x in xs), all(...)
xs.some(f), xs.every(f)
xs.Any(f), xs.All(f)
⚠min / max by key
Iteration
max(xs, key=f)
max(xs, default=0)
Math.max(...xs)  // RangeError
// past ~10⁵ items: use a loop
xs.MaxBy(f)  // .NET 6+
xs.Max()  // throws on empty
Python TypeScript C#
Heap heapq, a min-heap over a list none: write one PriorityQueue<TElement, TPriority>, min on priority, not stable
Sorted map / set none in stdlib (bisect on a list) none SortedSet<T>, SortedDictionary<K, V>
Integer size unbounded exact to 2⁵³ − 1, then bigint int 32-bit, wraps silently
-7 / 2 as an integer -7 // 2 == -4 (floor) Math.trunc(-7 / 2) === -3 -7 / 2 == -3 (truncate)
Composite keys tuples hash by value arrays compare by reference value tuples hash by value

Python and C# ship a min-heap. TypeScript doesn’t, so learn a 30-line one well enough to write it in five minutes. Ties are the subtle part: add a sequence number so equal priorities come out first-in, first-out. For when to use one, see heaps and priority queues.

heap.py
import heapq
if __name__ == "__main__":
# heapq is a MIN-heap over a plain list. Tuples compare left to right,
# so (priority, seq, item) gives "lowest priority first, then FIFO on ties".
pq: list[tuple[int, int, str]] = []
for seq, (prio, name) in enumerate([(2, "write"), (1, "read"), (2, "sync"), (0, "boot")]):
heapq.heappush(pq, (prio, seq, name)) # seq breaks ties and avoids comparing names
order = [heapq.heappop(pq)[2] for _ in range(len(pq))]
assert order == ["boot", "read", "write", "sync"]
# Max-heap: negate the key (works everywhere) ...
xs = [5, 1, 9, 3]
neg = [-x for x in xs]
heapq.heapify(neg) # O(n), in place
assert -heapq.heappop(neg) == 9
# ... or, on Python 3.14+, use the new *_max functions.
if hasattr(heapq, "heapify_max"):
mx = xs[:]
heapq.heapify_max(mx)
assert heapq.heappop_max(mx) == 9
assert heapq.nsmallest(2, xs) == [1, 3] and heapq.nlargest(2, xs) == [9, 5]
print("ok")

This is where the three languages disagree most, and where bugs are silent. See also math and number handling and money arithmetic.

numbers.py
import math
import sys
if __name__ == "__main__":
# Integers never overflow: they grow as needed.
assert 2**63 + 1 == 9223372036854775809
assert sys.maxsize == 2**63 - 1 # largest list index on 64-bit, not an int limit
# // FLOORS (rounds toward minus infinity) and % takes the sign of the divisor.
assert -7 // 2 == -4 and -7 % 2 == 1
assert divmod(-7, 2) == (-4, 1) # always a == b * q + r
# Truncation toward zero (what C# and JS do) needs math.trunc or int() on a float...
assert math.trunc(-7 / 2) == -3
# ...but / is float division: exact only up to 2**53.
assert (10**17 + 1) // 1 != int((10**17 + 1) / 1)
assert -(-7 // 2) == 4 # ceil division without floats
assert float("inf") > 10**100 # a safe "infinity" sentinel for min/max
assert math.isclose(0.1 + 0.2, 0.3) # never compare floats with ==
print("ok")

Rounding 2.5 is a classic: Python’s round() and C#’s Math.Round() use round half to even (2.5 → 2, 3.5 → 4) by default, while JavaScript’s Math.round() rounds half up toward +∞ (2.5 → 3, −2.5 → −2). For money, round explicitly with the rule the business wants.

Counter, Map with ?? 0, and GetValueOrDefault are the three spellings of the same idea. The trap is the key: a JS array or a C# int[] is compared by reference, so [1, 2] as a key never matches another [1, 2]. Use a string key in TypeScript and a value tuple in C#. See hash maps and counting.

keys_counts.py
from collections import Counter, defaultdict
if __name__ == "__main__":
words = "the cat and the hat and the bat".split()
# Counting: Counter is a dict subclass; missing keys read as 0.
counts = Counter(words)
assert counts["the"] == 3 and counts["dog"] == 0
# Sort by count descending, then word ascending: a tuple key does both.
ranked = sorted(counts.items(), key=lambda kv: (-kv[1], kv[0]))
assert ranked[:2] == [("the", 3), ("and", 2)]
# Grouping: defaultdict(list) creates the empty list on first access.
by_len: defaultdict[int, list[str]] = defaultdict(list)
for w in words:
by_len[len(w)].append(w)
assert by_len[3][:3] == ["the", "cat", "and"]
# Tuples are hashable, so they work as dict keys and set members (lists don't).
seen: set[tuple[int, int]] = {(0, 0), (1, 2)}
assert (1, 2) in seen
grid_cost = {(r, c): r * 10 + c for r in range(3) for c in range(3)}
assert grid_cost[(2, 1)] == 21
print("ok")

A FIFO queue is the engine of BFS. Python’s list.pop(0) and JavaScript’s shift() move every element, so a BFS that uses them is quadratic. Use deque, a head index, or Queue<T>. See queues and deques and stacks.

queue.py
from collections import deque
if __name__ == "__main__":
# deque: O(1) at both ends. list.pop(0) is O(n), so never use a list as a queue.
q: deque[int] = deque([1, 2, 3])
q.append(4) # push right
q.appendleft(0) # push left
assert q.popleft() == 0 and q.pop() == 4
assert q[0] == 1 and q[-1] == 3 # peek both ends (indexing the middle is O(n))
# maxlen makes a bounded buffer: appending to a full deque drops from the other end.
last3: deque[int] = deque(maxlen=3)
for x in range(6):
last3.append(x)
assert list(last3) == [3, 4, 5]
# Stack: a plain list. append / pop at the end are O(1).
stack = [1, 2]
stack.append(3)
assert stack.pop() == 3 and stack[-1] == 2
print("ok")
  • JavaScript’s default sort compares strings. [10, 9, 1].sort() returns [1, 10, 9]. Always pass (a, b) => a - b for numbers.
  • Stability. Python’s sort and JavaScript’s Array.prototype.sort (since ES2019) are stable. C#’s Array.Sort and List<T>.Sort are not (the docs call it an unstable introspective sort); LINQ OrderBy/ThenBy is stable.
  • Multi-key sorts. Python: a tuple key, key=lambda p: (-p.score, p.name). TypeScript: chain comparisons with ||. C#: OrderByDescending(...).ThenBy(...).
  • String order. Python and JS < compare code points / UTF-16 units. C#’s default string comparison can be culture-aware, so pass StringComparer.Ordinal when you mean “by character code”. Use a < b ? -1 : a > b ? 1 : 0 in TS when you want the same order as Python; localeCompare is locale-aware.
  • Comparator overflow. In C#, (a, b) => a - b can overflow for large ints. Use a.CompareTo(b).

More in sorting and custom comparators and sorted containers.