Dictionaries and Sets

EE1021 Programming for Engineers · Lecture 5 · 2026–2027

Basri Erdogan

Lecture Goals

  1. Store and retrieve related data with dictionaries
  2. Add, update, remove, and safely look up dictionary entries
  3. Explain that dictionary keys are unique and immutable
  4. Iterate over keys, values, and key–value pairs
  5. Build a frequency dictionary with an accumulation pattern
  6. Use sets for uniqueness, membership, and relationship operations
  7. Combine dictionaries and sets in a small engineering-data problem

Retrieval: Parallel Lists

sensor_ids = ["S1", "S2", "S3"]
readings = [4.92, 5.31, 4.97]
statuses = ["normal", "high", "normal"]

To find the status of S2, which assumptions must remain true about all three lists?

The lists must have equal lengths and matching order. A dictionary can connect each sensor ID directly to its data.

Choose the Data Structure

Choose list, tuple, dictionary, or set for each row.

# What we need to store Choice and reason
1 Voltages from one sensor, added as measured
e.g. 4.92, 5.08, 5.11, then 4.97 arrives
list samples.append(4.97): the list grows, and the measurement order is kept
2 A sensor’s ID and its unit, which never change
e.g. S1 and V
tuple ("S1", "V"): a small group of values that stays the same
3 The latest reading of each sensor, looked up by ID
e.g. S1 → 4.92, S2 → 5.08
dictionary {"S1": 4.92, "S2": 5.08}: give the ID, get the reading
4 The sensors that raised an alarm, each listed once
e.g. S2, S3, S2 → S2, S3
set {"S2", "S3"}: a repeated ID is kept only once

Part 1: Dictionaries

A Dictionary Maps Keys to Values

List lookup by position

index value
0 4.92
1 5.31
2 4.97

Dictionary lookup by meaning

key value
S1 4.92
S2 5.31
S3 4.97

A dictionary is a mutable mapping: key → value.

Create a Dictionary

readings = {
    "S1": 4.92,
    "S2": 5.31,
    "S3": 4.97,
}

empty_registry = {}
  • A colon separates each key from its value.
  • Commas separate entries.
  • Curly braces create the dictionary.

Retrieve by Key

readings["S1"]       # 4.92
readings["S2"]       # 5.31
readings["S9"]       # KeyError: "S9" is absent

Direct indexing is appropriate when the key must exist. A missing required key often indicates a program or data error.

Safe Lookup with get

readings.get("S2")         # 5.31
readings.get("S9")         # None
readings.get("S9", 0.0)    # 0.0

The optional second argument supplies a default without changing the dictionary.

When would a default value hide a genuine data error rather than handle an optional key?

Add or Update an Entry

readings = {"S1": 4.92, "S2": 5.31}

readings["S3"] = 4.97   # new key: add
readings["S2"] = 5.08   # existing key: replace value

After both assignments:

{"S1": 4.92, "S2": 5.08, "S3": 4.97}

Delete and Retrieve

readings = {"S1": 4.92, "S2": 5.08, "S3": 4.97}

del readings["S3"]
removed = readings.pop("S2")
fallback = readings.pop("S9", None)
  • del removes a required key and returns nothing.
  • pop removes a key and returns its value.
  • A default makes pop safe when the key may be absent.

What Does Membership Test?

readings = {"S1": 4.92, "S2": 5.08}

"S1" in readings             # True: tests keys
4.92 in readings             # False: not a key
4.92 in readings.values()    # True: explicitly tests values

Dictionary membership tests keys unless a values view is requested.

Keys Must Be Unique and Immutable

Common keys (immutable values):

  • strings and integers
  • tuples of strings and integers

Not valid as keys (mutable values):

  • lists and dictionaries
locations = {(2, 3): "sensor-A"}  # valid tuple key
# {[2, 3]: "sensor-A"}            # TypeError: a list cannot be a key

Avoid measured floating-point quantities as keys when exact equality is uncertain.

Duplicate Keys Replace Values

registry = {"S1": 4.90, "S2": 5.08, "S1": 4.92}
registry["S3"] = 4.97

print(registry)

Predict the entries and their values.

Check: {“S1”: 4.92, “S2”: 5.08, “S3”: 4.97}. A repeated key replaces its value.

Iterate Directly Over Keys

readings = {"S1": 4.92, "S2": 5.08, "S3": 4.97}

for sensor_id in readings:
    print(sensor_id, readings[sensor_id])

Direct iteration produces keys. Writing readings.keys() is valid but unnecessary here.

Iterate Over Values or Pairs

Values only

for value in readings.values():
    print(value)

Key–value pairs

for sensor_id, value in readings.items():
    print(sensor_id, value)

The pair from items() is unpacked into two loop variables.

Aggregate Dictionary Values

readings = {"S1": 4.92, "S2": 5.08, "S3": 4.97}

total = 0
for value in readings.values():
    total += value

average = total / len(readings)
print(round(average, 2))

The dictionary supplies meaningful lookup; its values can still be accumulated like a list.

Count Events: Explicit Pattern

events = ["normal", "high", "normal", "low", "normal"]
counts = {}

for event in events:
    if event in counts:
        counts[event] += 1
    else:
        counts[event] = 1

Each event is either a first occurrence or an update to an existing count.

Count Events with get

counts = {}

for event in events:
    counts[event] = counts.get(event, 0) + 1

print(counts)

Output: {“normal”: 3, “high”: 1, “low”: 1}

The default 0 represents a count before the first occurrence.

Dictionary Trace Checkpoint

counts = {}
for code in ["A", "B", "A"]:
    counts[code] = counts.get(code, 0) + 1

print(counts.get("A", 0), counts.get("C", 0))

Check: 2 0. The missing C key is not added by get.

Part 2: Sets

Create Sets — Including an Empty Set

active = {"S1", "S2", "S3"}
from_list = set(["S1", "S2", "S1"])

empty_set = set()
empty_dict = {}
  • Sets contain unique, immutable elements.
  • set(), not empty braces, creates an empty set.
  • Duplicate input elements collapse to one element.

Membership and Uniqueness

events = ["S1", "S2", "S1", "S3", "S2"]
unique_sensors = set(events)

"S2" in unique_sensors    # True
len(unique_sensors)        # 3

A set answers “Have we seen this?” and “How many distinct values?” without tracking positions.

Relationship Operations

active = {"S1", "S2", "S3"}
calibrated = {"S2", "S3", "S4"}

active | calibrated    # union: S1, S2, S3, S4
active & calibrated    # intersection: S2, S3
active - calibrated    # difference: S1
active ^ calibrated    # exactly one set: S1, S4

Read the expression as a relationship between groups, not as a numeric calculation.

Modify a Set Safely

faulty = {"S3"}

faulty.add("S5")
faulty.remove("S3")     # KeyError if absent
faulty.discard("S9")    # no error if absent

Use remove when absence is an error; use discard when absence is acceptable.

Set Checkpoint

active = {"S1", "S2", "S3"}
calibrated = {"S2", "S3", "S4"}

Write expressions for:

  1. sensors that are both active and calibrated;
  2. active sensors that still need calibration;
  3. sensors appearing in exactly one set.

Check: active & calibrated; active - calibrated; active ^ calibrated.

Part 3: Integrate the Structures

Sensor Fleet Report

readings = {"S1": 4.92, "S2": 5.31, "S3": 4.97}
calibrated = {"S1", "S3"}

status_counts = {}
for sensor_id, value in readings.items():
    status = "normal" if 4.75 <= value <= 5.25 else "alarm"
    status_counts[status] = status_counts.get(status, 0) + 1

needs_calibration = set(readings) - calibrated
  • The dictionary maps each sensor to its reading.
  • Another dictionary counts status categories.
  • Sets compare registered and calibrated sensor IDs.

Optional Bridge: Events from a File

The counting algorithm does not change when the event list comes from a file.

with open("sensor_events.txt", encoding="utf-8") as file:
    events = file.read().split()

counts = {}
for event in events:
    counts[event] = counts.get(event, 0) + 1

The lab includes sensor_events.txt. Paths, modes, exceptions, line iteration, and CSV are taught in Lecture 10.

Exit Ticket and Summary

Without running Python:

  1. Write a safe lookup for S9 that returns 0.0 if absent.
  2. What does for key in readings produce?
  3. Write the set expression for active but not calibrated sensors.

Check: readings.get(“S9”, 0.0); dictionary keys; active - calibrated.

Summary: dictionaries map meaningful keys to values; sets represent unique membership. Both solve problems that positional lists handle poorly.