← Home

058. List Indexing

Random access into ordered collections

058. List Indexing

After sorting the process snapshot by RSS, Aryan needs to pull specific entries by position — the worst offender, the top 3, the least-memory process as a sanity check:

ranked = sorted(snapshot, key=lambda p: p["rss_mb"], reverse=True)

worst   = ranked[0]    # highest memory — index 0
second  = ranked[1]    # second highest
least   = ranked[-1]   # lowest memory — negative index
recent  = ranked[-3:]  # last 3 (lowest consumers)

Coming from C, Aryan’s first instinct was ranked[len(ranked)] for the last element — Python raises IndexError because valid indices are 0 to len-1. The idiomatic fix is ranked[-1].

Negative indexing counts backward from the end:

index:  0      1       2     ...  -3     -2     -1
value: [512,  430,    210,  ...,   45,    32,    18]

He also uses index-based assignment to update a record in-place:

ranked[0]["alerted"] = True   # flag the worst offender

💡 Fun fact: Python’s negative indexing was inspired by APL (A Programming Language, 1960s) and is not found in C, Java, or most mainstream languages. It’s one of the features newcomers from those languages find most surprising — and then most useful. In NumPy (Python’s scientific computing library), negative indexing extends to multi-dimensional arrays: matrix[-1, -1] gets the bottom-right corner of any 2D array regardless of size.

⚠️ Watch out: lst[len(lst)] is always an IndexError — valid indices are 0 to len(lst)-1. This is the single most common beginner indexing mistake, imported directly from C habits where array[n] for an n-element array was just “undefined behavior.” In Python it’s an immediate, explicit crash. Use lst[-1] for the last element instead.

🤔 Think about it: ranked[0] gives the first element and ranked[-1] gives the last. But what does ranked[-0] give you — and why? What does this reveal about how Python handles negative zero in the context of indexing versus arithmetic?

Learning objectives

  • Use 0-based positive indices to access list elements
  • Use negative indices to access from the end
  • Avoid IndexError by using [-1] instead of [len-1]

Key concepts

  • list indexing
  • zero-based
  • negative indices

Try it

Concept detail

List indexing is 0-based: lst[0] is first, lst[1] is second, lst[n-1] is last. Negative indices count from the end: lst[-1] is last, lst[-2] is second-to-last.

Index map for a 5-element list: Positive: 0 1 2 3 4 Negative: -5 -4 -3 -2 -1

IndexError raised for out-of-range: lst[len(lst)] always fails. Use [-1] not [len(lst)-1] — cleaner and avoids computing length.

Swapping two elements: lst[a], lst[b] = lst[b], lst[a] # Python simultaneous assignment

Modification: lst[i] = value # replaces element at index i in-place

Reading is O(1) — lists use contiguous arrays internally.

Solution

def gold(scores):
    return scores[0]

def bronze(scores):
    return scores[2]

def last_place(scores):
    return scores[-1]

def swap_first_last(scores):
    result = scores.copy()
    result[0] = scores[-1]
    result[-1] = scores[0]
    return result

Tests

SCORES = [100, 85, 72, 60, 45]

def test_gold():
    assert gold(SCORES) == 100

def test_silver():
    assert SCORES[1] == 85  # verifying test data

def test_bronze():
    assert bronze(SCORES) == 72

def test_last_place():
    assert last_place(SCORES) == 45

def test_last_place_two_elements():
    assert last_place([10, 20]) == 20

def test_swap_first_last():
    result = swap_first_last([1, 2, 3, 4, 5])
    assert result[0] == 5
    assert result[-1] == 1
    assert result[2] == 3  # middle unchanged

def test_original_unchanged():
    original = [10, 20, 30]
    swap_first_last(original)
    assert original == [10, 20, 30]

Resources