Choosing the Right Type

Concepts covered: Data Types

Choosing a collection comes down to 4 questions. Order and change were covered in the earlier sections, as were duplicates, and the question about lookup by label is what a dict's keys are for. Ask them in the order below and the answer usually falls out by the second or third.

The questions narrow the field one at a time. Take a version number such as 3.9.7, stored as 3 integers. Position matters, since 3 means the major version only in slot 0, so question 1 leaves a list or a tuple. The parts of a released version never change, so question 2 leaves the tuple. Now take the error count for each pipeline stage. No stage is first or third in any meaningful sense, so question 1 rules out list and tuple. The counts rise as errors arrive, so question 2 allows a dict or a set, and question 3 settles it: you read a count by the stage's name, which makes it a dict. The table pairs more needs with the collection that fits.

NeedPickBecause
Steps of a pipeline that run in sequence and may growlistorder matters and steps get added
Latitude and longitude of one warehousetuple2 fixed values, usable as a dict key
Stock level per product namedictlookup by label
User ids already emailed todaysetmembership test, no repeats
The 30 tracks of a playlist in play orderlistorder matters and tracks move
Column names a header row must matchtuplea fixed fact nothing should edit
Reach for a list when
  • Arrival order carries meaning
  • Repeated values are real data
  • You read items by slot number
  • Items are appended at the end
Reach for a set when
  • The only question is whether a value is present
  • Repeated values should vanish
  • Order is irrelevant
  • The items are numbers or strings (tuples work too)

Putting 3 types together

Real tasks mix types, and the mix follows the same questions. The animation walks a list of click events, uses a set to remember which users have already appeared, and builds a list of each user's first event. The list preserves arrival order, the set answers not in without scanning, and the dict inside each event holds that event's fields by name.

events = [
    {"user": 1, "action": "click"},
    {"user": 2, "action": "view"},
    {"user": 1, "action": "buy"},
    {"user": 3, "action": "click"},
    {"user": 2, "action": "click"},
]

seen_users = set()
first_events = []

for event in events:
    user = event["user"]
    if user not in seen_users:
        seen_users.add(user)
        first_events.append(event)

print("events:", len(events))
print("users:", len(seen_users))
print("first:", first_events)
events: 5
users: 3
first: [{'user': 1, 'action': 'click'}, {'user': 2, 'action': 'view'}, {'user': 3, 'action': 'click'}]

Swap the set for a list and the program still works but scans every earlier user on each event. Swap the first-events list for a set and the arrival order is gone.

Do
  • Run through the 4 questions before writing the first bracket.
  • Use a tuple for a record whose shape never changes.
  • Keep a set beside a list when you need both arrival order and a fast membership test.
  • Count repeats with a dict: the item is the key and the count is the value.
Avoid
  • Rely on the order a set prints in.
  • Index a dict with a position number.
  • Scan a list with in when a set would answer the same question without a scan.
  • Repeat a key in a dict literal and expect both values to survive.

Capstones

These mix every idea so far: order and change, plus conversion and duplicates. Trace the code by hand first; the explanations assume you did.

What does this program print?

batches = ["b1", "b2", "b1", "b3"]
done = set()
order = []
for batch in batches:
    if batch not in done:
        done.add(batch)
        order.append(batch)
print(order, len(done))

That capstone kept a set beside a list so each answered its own question. The next one hands you a need and no code. Run the 4 questions on it in order, and when 2 types both satisfy every answer, prefer the one that refuses what the program never does: a type that blocks an unwanted edit turns a silent mistake into an error on the line that made it.

A program stores the 7 day names and later reads the name for day number 3 with names[3]. The program never edits the day names after they are stored. Which collection fits best?

The third capstone builds 2 collections from the same list of readings, and each answers a different count. len of a set counts distinct values, while len of a tuple counts every reading, repeats included. max returns the same highest value from either one, because dropping repeats never removes the largest value, only extra copies of it.

The last capstone depends on what square brackets mean on a dict built from pairs. dict() makes the first item of each pair a key, so a dict built from (name, quantity) pairs is read with a name. A position number between the brackets asks for a key that is that number, and when no such key exists the lookup stops with a KeyError naming the missing key.

About this lesson

This section is part of the Data Structures: Beginner lesson on DataDriven, the home of data engineering. Lessons cover SQL, Python, Spark, data modeling and pipeline architecture, and each section pairs its explanation with worked examples and practice you run as you go.