Skip to content

Week 2 · Python for real work and SQL

Collections and loops

Choose between lists, tuples, sets and dictionaries, split and join text, sort without surprises, loop over any collection and clean up messy records.

Live session

You already know lists and dictionaries from week 1, and tuples from Mutable and immutable objects. This page adds the fourth collection, the set, and then covers what you'll do with collections every day: move between text and lists, sort them, loop over them and clean them up.

How to follow along

Work in your learn-python project. Create a file called collections_demo.py, type each example into it and run it with:

uv run collections_demo.py

Don't call the file collections.py. Python would find your file instead of the standard library's collections module, which can break your imports.

Compare what you see with the Output under each example, and clear the file before you type the next one.

Four ways to hold many values

TypeWritten asKeeps its orderCan changeDuplicatesGood for
list[4500, 12800]YesYesAllowedItems in order: today's orders, rows from a file
tuple(24.86, 67.01)YesNoAllowedA small, fixed record: coordinates, a pair of values
set{"low", "high"}NoYesRemovedUnique values, and checking whether something is present
dict{"id": "A101"}Yes, the order you added keysYesKeys are uniqueNamed fields, and data from JSON

A list can hold values of different types, such as ["Rice", 2, True], but it's usually clearer to keep one type in a list and use a dictionary for a record with different fields.

Sets

A set holds unique values with no order. Add a duplicate and it's simply ignored:

collections_demo.py
priorities = {"low", "medium", "urgent", "low"}

print(len(priorities))
print("urgent" in priorities)
print("critical" in priorities)
print(sorted(priorities))

Output:

3
True
False
['low', 'medium', 'urgent']

Now add print(priorities) and run the file a few times. The order can change from one run to the next, because a set doesn't keep one. That's why these examples use sorted(), which gives you a list in a predictable order.

Removing duplicates

The most common use of a set is finding the unique values in a list:

collections_demo.py
customers = ["Noor Foods", "Bilal Traders", "Noor Foods", "Karachi Crafts"]
unique_customers = set(customers)

print(len(customers))
print(len(unique_customers))
print(sorted(unique_customers))

Output:

4
3
['Bilal Traders', 'Karachi Crafts', 'Noor Foods']

Creating an empty set

{} creates an empty dictionary, not a set. Use set(), then add values with .add():

collections_demo.py
print(type({}))

seen = set()
seen.add("A101")
seen.add("A101")
print(seen)

Output:

<class 'dict'>
{'A101'}

Comparing two sets

Sets can answer questions about two groups directly:

collections_demo.py
this_week = {"Noor Foods", "Bilal Traders", "Karachi Crafts"}
last_week = {"Noor Foods", "Zara Textiles"}

print(sorted(this_week & last_week))
print(sorted(this_week - last_week))
print(sorted(this_week | last_week))

Output:

['Noor Foods']
['Bilal Traders', 'Karachi Crafts']
['Bilal Traders', 'Karachi Crafts', 'Noor Foods', 'Zara Textiles']
  • & gives the values in both sets: customers who ordered both weeks.
  • - gives the values in the first set only: customers who ordered this week but not last week.
  • | gives the values in either set: everyone who ordered at all.

Splitting and joining text

Text and lists work together. .split() turns a string into a list, and .join() turns a list of strings back into one string:

collections_demo.py
line = "A101,Noor Foods,4500"
parts = line.split(",")
print(parts)
print(parts[1])

print("noor   foods  karachi".split())

items = ["rice", "oil", "sugar"]
print(", ".join(items))

Output:

['A101', 'Noor Foods', '4500']
Noor Foods
['noor', 'foods', 'karachi']
rice, oil, sugar
  • split(",") splits at every comma. Every part is still text, including '4500'.
  • split() with nothing inside the brackets splits on any run of spaces.
  • .join() is called on the separator, not on the list, and it joins only strings.

Together they make a useful cleaning pattern: split a value into parts, clean each part, and join them back together.

collections_demo.py
raw_name = "  amna   RAZA "

cleaned = []
for word in raw_name.split():
    cleaned.append(word.title())

print(" ".join(cleaned))

Output:

Amna Raza

Sorting: changing the list or making a new one

.sort() and .reverse() change the list itself. sorted() leaves it alone and returns a new list:

collections_demo.py
names = ["Noor", "Amna", "Bilal"]

print(sorted(names))
print(names)

names.sort()
print(names)

names.reverse()
print(names)

Output:

['Amna', 'Bilal', 'Noor']
['Noor', 'Amna', 'Bilal']
['Amna', 'Bilal', 'Noor']
['Noor', 'Bilal', 'Amna']

Use sorted() when other code still needs the original order, or when you're sorting something that isn't a list. It works on tuples, sets and dictionaries too, and always gives you back a list.

Looping over collections

A for loop works on every collection. Lists and tuples give you items in order. A set gives you each item once, in no particular order, so sort it first if the order matters:

collections_demo.py
for total in [4500, 12800]:
    print(total)

for status in ("planned", "in_progress", "done"):
    print(status)

for priority in sorted({"low", "urgent", "medium"}):
    print(priority)

Output:

4500
12800
planned
in_progress
done
low
medium
urgent

Looping over a dictionary gives you its keys. Use .values() for just the values, or .items() for both at once:

collections_demo.py
order = {"id": "A101", "customer": "Noor Foods", "total": 4500}

for key in order:
    print(key)

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

for key, value in order.items():
    print(f"{key}: {value}")

Output:

id
customer
total
A101
Noor Foods
4500
id: A101
customer: Noor Foods
total: 4500

.keys() also gives you just the keys, which is the same as looping over the dictionary directly. In for key, value in order.items(), each item is a (key, value) tuple, and the loop unpacks it into two variables.

Putting it together: cleaning messy records

Real data rarely arrives clean. A spreadsheet export or form often gives you a list of dictionaries with stray spaces, mixed capitals and 1 or 0 instead of True or False. A normal first step is to build a clean list before you do anything else with it:

collections_demo.py
raw_orders = [
    {"customer": " noor foods ", "status": "Delivered", "paid": 1},
    {"customer": "BILAL TRADERS", "status": " pending ", "paid": 0},
    {"customer": "noor foods", "status": "PENDING", "paid": 0},
]

orders = []
for raw in raw_orders:
    orders.append(
        {
            "customer": raw["customer"].strip().title(),
            "status": raw["status"].strip().lower(),
            "paid": bool(raw["paid"]),
        }
    )

for order in orders:
    print(order)

customers = set()
for order in orders:
    customers.add(order["customer"])
print(sorted(customers))

Output:

{'customer': 'Noor Foods', 'status': 'delivered', 'paid': True}
{'customer': 'Bilal Traders', 'status': 'pending', 'paid': False}
{'customer': 'Noor Foods', 'status': 'pending', 'paid': False}
['Bilal Traders', 'Noor Foods']

bool() turns 1 into True and 0 into False. The code builds new dictionaries and leaves raw_orders untouched, so you can always compare the result with what came in. Without the cleaning, " noor foods " and "noor foods" would count as two different customers.