Building Lists with Loops

Introduction: From One Answer to a Whole Collection

Welcome back to Solving Problems with Loop Patterns in Python! In Unit 1, every pattern we built ended the same way: one loop, one tracking variable, and one answer left behind at the end, whether that was a maximum, an index, or a count.

Plenty of real tasks, though, ask for something bigger than a single number. A store needs a full price list with tax already applied; a manager wants a shortlist of only the expensive items; a signup form needs every name cleaned up and capitalized. In each case, the answer is another sequence, not a single value.

The good news is that the skeleton barely changes: our tracking variable simply becomes a list that grows one item at a time. Here is the data we will work with and the output we are building toward:

prices = [19.99, 5.50, 42.00, 8.75]
With tax: [21.59, 5.94, 45.36, 9.45]
Expensive: [19.99, 42.0]

The Build-a-List Skeleton: Empty List and append()

Every pattern in this lesson reuses the same three-step shape:

  1. Before the loop: create an empty list with [].
  2. Inside the loop: call append() to add one item at a time.
  3. After the loop: read the finished list, now holding all collected items.

The key tool is list.append(value), which adds a single item to the end of a list and grows its length by one. It modifies the list in place and returns nothing, so we write results.append(x) on its own line; writing results = results.append(x) would throw the list away and leave us with None.

The empty list must exist before the loop for the same reason our sentinels did: a list created inside the loop would be rebuilt from scratch on every iteration, and if the loop never ran, the variable would not exist at all. Notice also that the source list is never touched: we read from one list and write into another.

Pattern 1: Mapping Every Element to a Transformed Value

Our first pattern is mapping: the same number of items, but new values. Every element of the source is passed through the same calculation, and the result is stored in the new list.

prices = [19.99, 5.50, 42.00, 8.75]

# Map each element to a transformed value
with_tax = []
for price in prices:
    with_tax.append(round(price * 1.08, 2))

with_tax starts empty. On each iteration, price holds one value from the source, and the expression round(price * 1.08, 2) is evaluated first: it adds 8% tax and rounds to two decimal places so the amount reads like real money. Only then is that computed value appended.

priceround(price * 1.08, 2)with_tax so far
19.9921.59[21.59]
5.505.94[21.59, 5.94]
42.0045.36[21.59, 5.94, 45.36]
8.759.45[21.59, 5.94, 45.36, 9.45]

Because every iteration appends exactly once, mapping comes with a guarantee: the result always has exactly as many items as the source — here, four in and four out.

For these beginner exercises, we use ordinary floating-point numbers and round() so the focus stays on loop patterns. Real financial software often needs stricter rules and exact decimal calculations. It commonly stores money as integer minor units, such as cents, or uses Python’s decimal.Decimal type.

Mapping Strings, Too

Mapping is not tied to arithmetic. The expression inside append() can be anything that produces a value, including a string method, so the same pattern cleans up text just as easily.

names = ["ada", "grace", "alan"]

upper_names = []
for name in names:
    upper_names.append(name.upper())

Compare this to the previous snippet: the empty list, the for line, and the append() call are structurally identical. The only thing that changed is the expression handed to append(): name.upper() instead of a rounded multiplication. That portability is what makes the pattern worth memorizing; once the skeleton is in our fingers, switching data types is a one-line edit. Here, upper_names ends up as ["ADA", "GRACE", "ALAN"], still three items long.

Pattern 2: Collecting Only Elements That Pass a Test

Our second pattern is filtering: the same values, possibly in fewer items. Nothing is transformed here; we simply decide, element by element, whether each item belongs in the new list.

# Collect only elements that pass a test
expensive = []
for price in prices:
    if price > 10:
        expensive.append(price)

The structural difference from mapping is the extra level of indentation: append() now sits inside an if, so some iterations add nothing at all. As we walk through the data, 19.99 passes and is kept; 5.50 fails and is skipped; 42.00 passes; and 8.75 fails. The result is [19.99, 42.0], two items from a source of four.

That 42.0 is not a bug: Python drops the trailing zero when displaying floats, so 42.00 and 42.0 are the same number. As you may recall from the for-loops course, we used this exact condition to count matching elements; the only change now is that we keep the values themselves instead of just tallying them.

Mapping vs. Filtering: Spotting the Difference

The two patterns look similar on screen, so it helps to name precisely what separates them:

MappingFiltering
Purposechange every valueselect some values
Where append() sitsdirectly in the loop bodyinside an if
Value storeda computed expressionthe element, unchanged
Result lengthsame as the sourcesame or shorter

A few mistakes show up often, and all of them are quiet ones:

  • Indenting append() outside the if in a filter, which keeps every element instead of the matching ones.
  • Writing result = value instead of result.append(value), so only the last item survives.
  • Printing inside the loop rather than appending, which looks correct on screen but leaves no list to reuse.
  • Resetting result = [] inside the loop, which discards everything gathered so far on each pass.

Combining Both: Filter First, Then Transform

The two patterns compose naturally: use an if to choose the elements, then hand a transformed value to append(). Suppose we want a 10% discount, but only on the expensive items.

discounted = []
for price in prices:
    if price > 10:
        discounted.append(round(price * 0.9, 2))

Read it as two questions, in this order: Which elements do I keep? (Those above 10.) What do I store for each kept one? (Its discounted value.) Only 19.99 and 42.00 reach the append(), so discounted becomes [17.99, 37.8]. Here is the full program with our two original patterns side by side:

prices = [19.99, 5.50, 42.00, 8.75]

# Map each element to a transformed value
with_tax = []
for price in prices:
    with_tax.append(round(price * 1.08, 2))

# Collect only elements that pass a test
expensive = []
for price in prices:
    if price > 10:
        expensive.append(price)

print("With tax:", with_tax)
print("Expensive:", expensive)

Each block reads the same source list and leaves its own finished list behind, exactly as targeted:

With tax: [21.59, 5.94, 45.36, 9.45]
Expensive: [19.99, 42.0]

Conclusion and Next Steps

We now have one more skeleton in our toolkit: create an empty list before the loop, use append() inside it (conditionally or not), and read the result after the loop ends. From that single shape came three variations: mapping every element to a new value, filtering to keep only what passes a test, and filter-then-map, which does both in one pass. A useful rule of thumb for checking our work is that mapping preserves the item count, while filtering can only shrink it.

Your practice is next: scaling a list of numbers, filtering a list by a test, uppercasing a list of strings, and finally combining a condition with a transformation. For every task, two questions will point you straight at the solution: What exactly goes into my new list? and Does that append() belong inside an if?

After that, we will place loops inside other loops, which opens the door to pairing elements together and walking through grids of data.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal