Iterators
Indexing
Programming
Data Access
Algorithms

Acessing Values only at certain indexes using iterators

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Iterators are designed for sequential access, so “give me items at indexes 3, 10, and 25” is not as direct as it is with a list. The correct approach depends on whether the requested indexes are sorted, whether you only need one pass, and whether materializing the full iterator is acceptable. In many cases, the best answer is to consume the iterator progressively rather than trying to force random access onto a sequential abstraction.

Remember What an Iterator Is

An iterator yields values one by one and forgets the ones it has already passed. That means there is no built-in constant-time jump to an arbitrary position the way there is with a list.

python
items = iter(["a", "b", "c", "d", "e"])
print(next(items))
print(next(items))

After two calls to next, the iterator has advanced. There is no built-in way to “go back” unless you created another iterator from the original source.

That is why accessing specific indexes from an iterator is really about controlled consumption.

If the Indexes Are Sorted, Use islice

When the desired indexes are in increasing order, itertools.islice is a natural fit.

python
1from itertools import islice
2
3items = iter([10, 20, 30, 40, 50, 60])
4indexes = [1, 3, 5]
5
6result = []
7current = 0
8for target in indexes:
9    value = next(islice(items, target - current, target - current + 1))
10    result.append(value)
11    current = target + 1
12
13print(result)

This works by consuming only as much of the iterator as needed to reach each next target index.

The important condition is that the indexes must be increasing. You cannot ask for index 5 and then later index 2 from the same one-way iterator.

enumerate Works Well for One-Pass Filtering

If you do not mind scanning the iterator from start to finish once, enumerate is often the simplest approach.

python
1items = iter([10, 20, 30, 40, 50, 60])
2wanted = {1, 3, 5}
3
4selected = [value for idx, value in enumerate(items) if idx in wanted]
5print(selected)

This is very readable and often good enough. It does examine every item up to the largest requested index, but that is unavoidable for a forward-only iterator anyway.

Using a set for wanted keeps membership tests efficient.

Materialize Only When Random Access Really Matters

If you need repeated arbitrary index access, the honest answer may be that an iterator is the wrong abstraction for the downstream task. Converting to a list is then the cleanest move.

python
items = iter([10, 20, 30, 40, 50, 60])
cache = list(items)
print(cache[1], cache[3], cache[5])

This trades memory for convenience. If the sequence is small or repeated random access is needed, that tradeoff is often worth it.

If the sequence is huge or infinite, materializing it is obviously not an option, and one-pass iterator techniques become necessary.

Be Honest About the Problem Shape

There are really three different problems hiding under this question.

  1. One-pass access to a few increasing indexes.
  2. One-pass filtering by position while consuming the whole iterator.
  3. Repeated random access.

The correct solution changes with the problem shape. Trying to use one technique for all three usually leads to confusing code.

A Reusable Helper for Sorted Indexes

If you often need the first problem, a helper makes the behavior explicit.

python
1from itertools import islice
2
3
4def values_at_sorted_indexes(iterable, indexes):
5    it = iter(iterable)
6    result = []
7    current = 0
8
9    for target in indexes:
10        if target < current:
11            raise ValueError("indexes must be sorted in ascending order")
12        result.append(next(islice(it, target - current, target - current + 1)))
13        current = target + 1
14
15    return result
16
17print(values_at_sorted_indexes([10, 20, 30, 40, 50, 60], [1, 3, 5]))

This makes the sequential nature of the operation explicit instead of pretending the iterator supports true indexing.

Common Pitfalls

  • Treating an iterator as though it supports random access like a list.
  • Requesting indexes out of order from a one-way iterator and expecting the earlier values to still be available.
  • Forgetting that consuming values to reach one target index also advances past all earlier positions.
  • Materializing a huge iterator into a list when a one-pass solution would have been enough.
  • Using a list instead of a set for membership checks in an enumerate-based filter when the index set is large.

Summary

  • Iterators support sequential consumption, not true random indexing.
  • For increasing target indexes, itertools.islice is a good fit.
  • For one full pass with selected positions, enumerate plus an index set is simple and effective.
  • If you need repeated arbitrary access, converting to a list may be the correct design choice.
  • The best solution depends on whether the problem is one-pass sequential access or real random access.

Course illustration
Course illustration

All Rights Reserved.