← all topics

File-parsing labs Code

Ten original Python labs shaped like the NPE coding screen · fixtures inline · expected output hidden until you try

Work each lab in a blank editor with a timer. Save the fixture text to a file, write a script that reads it, print your result as JSON and only then open the expected output. Say the complexity out loud. Then log the attempt on topic 5, topic 15 or the tracker.

Lab 1 · Count host failures from a file 25 min

Read a UTF-8 host,status file without a header. Ignore blank lines. Strip whitespace; normalize hostnames to lowercase and statuses to uppercase. Valid statuses are UP/DOWN. Report the failure count for every host seen in a valid row, including zero, plus the 1-based line numbers of malformed rows. A valid row has exactly two nonempty fields.

Skills: with open · line iteration · dict counts · validation

events.csv

WEB-01,UP
db-01,DOWN
web-01,DOWN

badline
web-01,DOWN
cache-01,UP

Edge cases to test: empty file, only UP records, unknown status, extra separator, blank hostname.

Expected output
{
 "failures": {
  "web-01": 2,
  "db-01": 1,
  "cache-01": 0
 },
 "invalidLines": [
  5
 ]
}

Target complexity: O(n) input processing; O(u + e) output/state for distinct hosts and error lines.

Follow-up: For 20 GB of input, what grows with file size? If error output is also huge, stream errors to another file.

Lab 2 · Aggregate quoted CSV fields 25 min

Read CSV with interface,bytes columns. Interface names may contain commas inside quoted fields. Sum nonnegative integer bytes per interface. Report invalid data-row numbers, counted from one after the header. Do not use split(',').

Skills: csv.DictReader · numeric parsing · aggregation

traffic.csv

interface,bytes
"edge,west",100
eth0,25
"edge,west",50
eth0,oops
eth1,-1

Edge cases to test: quoted comma, zero bytes, negative number, missing field, non-integer number.

Expected output
{
 "bytes": {
  "edge,west": 150,
  "eth0": 25
 },
 "invalidDataRows": [
  4,
  5
 ]
}

Target complexity: O(n) rows, O(u + e) state/output.

Follow-up: Write the report as CSV and explain newline='' when using Python's csv module.

Lab 3 · Keep the latest health record 30 min

Read JSON Lines; each nonblank line is one object with host,ts,status. ts is an integer, status is UP/DOWN and host is nonempty. Keep the largest ts per host; ties use the later input line. Return downHosts sorted alphabetically and latestTs per valid host. Count invalid nonblank lines. Normalize hostnames to lowercase.

Skills: json.loads · nested records · dictionary replacement · tie policy

health.jsonl

{"host":"web","ts":20,"status":"UP"}
{"host":"web","ts":10,"status":"DOWN"}
{"host":"db","ts":15,"status":"UP"}
{"host":"db","ts":15,"status":"DOWN"}
{bad json}

Edge cases to test: out-of-order timestamps, equal timestamps, missing field, malformed JSON, JSON array instead of object.

Expected output
{
 "downHosts": [
  "db"
 ],
 "latestTs": {
  "web": 20,
  "db": 15
 },
 "invalidLines": 1
}

Target complexity: O(n + u log u) including sorted output; O(u) state.

Follow-up: Explain why a single JSON array and JSON Lines have different streaming requirements.

Lab 4 · Find the busiest clients 30 min

Read a whitespace-delimited file with one client IP string per line. Treat each nonblank line as an identifier. Return the k most frequent identifiers ordered by descending count then ascending identifier. k may exceed the number of distinct values; k=0 returns an empty list.

Skills: Counter · sorting keys · top k · complexity

clients.txt

192.0.2.2
192.0.2.1
192.0.2.2
192.0.2.3
192.0.2.1
192.0.2.2

Parameters: {"k": 2}

Edge cases to test: all counts tied, k=0, empty file, k larger than unique count.

Expected output
[
 [
  "192.0.2.2",
  3
 ],
 [
  "192.0.2.1",
  2
 ]
]

Target complexity: Start O(n + u log u); discuss O(n + u log k) ranking with a size-k heap.

Follow-up: Do not claim O(k) total memory: the frequency map still stores u distinct keys.

Lab 5 · Compare two configuration snapshots 30 min

Each JSON file maps interface names to objects containing mtu and adminUp. Return sorted added, removed and changed interface names. For interfaces in both snapshots, changed means either of those two fields differs. Assume this lab's input schema is valid; extra fields should not affect the result.

Skills: json.load · sets · dictionary lookup · stable output

before.json

{"eth0":{"mtu":1500,"adminUp":true},"eth1":{"mtu":1500,"adminUp":true},"eth2":{"mtu":9000,"adminUp":false}}

after.json

{"eth0":{"mtu":9000,"adminUp":true},"eth2":{"mtu":9000,"adminUp":false},"eth3":{"mtu":1500,"adminUp":true}}

Edge cases to test: identical snapshots, one empty snapshot, boolean change only, ignored extra field.

Expected output
{
 "added": [
  "eth3"
 ],
 "removed": [
  "eth1"
 ],
 "changed": [
  "eth0"
 ]
}

Target complexity: O(a+b) comparison plus sorting output; O(a+b) loaded snapshots.

Follow-up: How would you report which fields changed without mutating either input?

Lab 6 · Track consecutive failures per host 35 min

Read host,status records in observation order. Records from different hosts can interleave. Return each host's longest consecutive run of DOWN observations in that host's own subsequence. UP resets only that host's current run. Assume valid input.

Skills: per-key state · single pass · invariants

status.csv

a,DOWN
b,DOWN
a,DOWN
a,UP
b,DOWN
a,DOWN
b,UP

Edge cases to test: all UP, all DOWN, interleaved hosts, last run is longest, one observation.

Expected output
{
 "a": 2,
 "b": 2
}

Target complexity: O(n) time, O(u) state.

Follow-up: Explain why storing only one global run counter fails.

Lab 7 · Join inventory with traffic 35 min

inventory.csv contains unique host,region rows. traffic.csv contains host,bytes rows, possibly repeated. Sum bytes by region. Unknown hosts go into an UNKNOWN region. The inputs have valid schemas and nonnegative integer bytes. Return the region totals.

Skills: two-file join · hashmap indexing · aggregation · missing keys

inventory.csv

host,region
a,us
b,eu
c,us

traffic.csv

host,bytes
a,10
b,20
a,5
d,7

Edge cases to test: unknown host, inventory host without traffic, empty traffic file, repeated host.

Expected output
{
 "us": 15,
 "eu": 20,
 "UNKNOWN": 7
}

Target complexity: O(i+t) time; O(i+r) working state with inventory indexed and traffic streamed.

Follow-up: Why is repeatedly rescanning inventory for each traffic row wasteful?

Lab 8 · Match addresses to the most specific prefix 40 min

routes.csv contains unique valid IPv4 network CIDRs and labels. destinations.txt contains valid IPv4 addresses. For each address return the label of its most specific matching prefix, or null if none match. Use Python's standard ipaddress module. Preserve destination order.

Skills: ipaddress · parsing · longest-prefix match · correctness before optimization

routes.csv

prefix,label
0.0.0.0/0,default
192.0.2.0/24,site
192.0.2.128/26,rack

destinations.txt

192.0.2.130
192.0.2.10
198.51.100.9

Edge cases to test: /32 host route, no matching route, overlapping prefixes, boundary address.

Expected output
[
 "rack",
 "site",
 "default"
]

Target complexity: O(q*r) straightforward scan with O(r) parsed routes. Discuss a trie only after the working solution.

Follow-up: Write the same membership check for one IPv4 prefix using integers and a mask.

Lab 9 · Merge two sorted log files 35 min

Two files each contain sorted timestamp,message CSV rows without headers. Timestamps are nondecreasing integers. Merge into one output stream in ascending timestamp order; equal timestamps from the left file come first. Preserve within-file order and do not load the entire files.

Skills: iterators · two pointers · stable ordering · streaming output

left.csv

1,a
3,c
5,e

right.csv

2,b
3,d
6,f

Edge cases to test: one empty file, all timestamps equal, one file exhausted early, negative timestamps.

Expected output
[
 [
  1,
  "a"
 ],
 [
  2,
  "b"
 ],
 [
  3,
  "c"
 ],
 [
  3,
  "d"
 ],
 [
  5,
  "e"
 ],
 [
  6,
  "f"
 ]
]

Target complexity: O(a+b) time and O(1) working records, excluding output if streamed.

Follow-up: Extend to k sorted logs with a heap and explain tie-breaking.

Lab 10 · Detect repeated failures in a time window 45 min

Read timestamp,host,status rows sorted by timestamp. At each DOWN row, include all DOWN events for that host with timestamp in the inclusive interval [t-window,t]. Emit [t,host,count] whenever that count is at least threshold. UP rows do not reset or remove earlier events; they do not produce alerts. Assume valid rows.

Skills: deque · per-host state · sliding window · boundary tests

events.csv

0,a,DOWN
2,b,DOWN
4,a,DOWN
6,a,UP
10,a,DOWN
11,a,DOWN

Parameters: {"window": 10, "threshold": 3}

Edge cases to test: event exactly at t-window, multiple events with same timestamp, interleaved hosts, UP during window, no alerts.

Expected output
[
 [
  10,
  "a",
  3
 ],
 [
  11,
  "a",
  3
 ]
]

Target complexity: Each DOWN event enters and leaves a deque at most once: O(n) time. State depends on retained events and host count, not automatically O(1).

Follow-up: Use this as a 45-minute rehearsal: clarify for 5 minutes, implement/test for 30, explain complexity and a modification for 10.

← 5 · File handling · all topics · tracker →