Iterator pattern in LLD - System Design Guide
Start learning this pattern below
Jump into concepts and practice - no test required
┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ Client │──────▶│ Iterator │──────▶│ Collection │ │ (uses next()) │ │ (next(), hasNext())│ │ (data storage)│ └───────────────┘ └───────────────┘ └───────────────┘
This diagram shows the client using an iterator to traverse a collection without accessing its internal structure directly.
### Before (without Iterator pattern): class Numbers: def __init__(self): self.data = [1, 2, 3, 4, 5] # Client accesses internal data directly nums = Numbers() for i in range(len(nums.data)): print(nums.data[i]) ### After (with Iterator pattern): class Numbers: def __init__(self): self.data = [1, 2, 3, 4, 5] def __iter__(self): return NumbersIterator(self.data) class NumbersIterator: def __init__(self, data): self._data = data self._index = 0 def __next__(self): if self._index < len(self._data): result = self._data[self._index] self._index += 1 return result else: raise StopIteration def __iter__(self): return self # Client uses iterator without accessing internal data nums = Numbers() for num in nums: print(num)
Practice
What is the main purpose of the Iterator pattern in system design?
Solution
Step 1: Understand the role of Iterator pattern
The Iterator pattern is designed to provide a way to access elements of a collection one by one without revealing the internal structure of the collection.Step 2: Compare with other options
Options B, C, and D describe unrelated design patterns or system functions such as data storage, object cloning, and security management.Final Answer:
To provide a way to access elements of a collection sequentially without exposing its underlying structure -> Option DQuick Check:
Iterator pattern = Access collection without exposing structure [OK]
- Confusing Iterator with data storage or cloning patterns
- Thinking Iterator manages security or authentication
- Assuming Iterator modifies the collection
Which of the following is the correct method signature for the next() method in an iterator interface?
Solution
Step 1: Recall the standard iterator method signature
Thenext()method typically takes no parameters except the implicit self and returns the next element in the collection.Step 2: Analyze each option
def next(self) -> Element matches the standard signature: it takes self and returns an element. Options B and D incorrectly add parameters, and C returns void which is incorrect.Final Answer:
def next(self) -> Element -> Option CQuick Check:
next() takes no args, returns element [OK]
- Adding parameters to next() method
- Returning void instead of element
- Confusing next() with hasNext() method
Consider the following Python code implementing a simple iterator:
class MyIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index < len(self.data):
result = self.data[self.index]
self.index += 1
return result
else:
raise StopIteration
it = MyIterator([10, 20, 30])
print(next(it))
print(next(it))What will be the output?
Solution
Step 1: Trace the iterator's next calls
First call to next(it) returns data[0] = 10 and increments index to 1. Second call returns data[1] = 20 and increments index to 2.Step 2: Confirm no errors occur
Since index is less than length during both calls, no StopIteration is raised.Final Answer:
10 20 -> Option BQuick Check:
First two elements printed: 10 and 20 [OK]
- Assuming next() skips elements
- Expecting error before StopIteration
- Mixing up index increments
Given this iterator implementation in Python, identify the bug:
class BuggyIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index <= len(self.data):
result = self.data[self.index]
self.index += 1
return result
else:
raise StopIterationWhat is the cause of the error when iterating?
Solution
Step 1: Analyze the condition in __next__
The condition uses <= len(self.data), which allows index to equal length, causing out-of-range access.Step 2: Understand the error caused
Accessing self.data[self.index] when index == len(self.data) causes IndexError because list indices go from 0 to len-1.Final Answer:
IndexError due to accessing out-of-range element -> Option AQuick Check:
Condition allows index == length causing IndexError [OK]
- Using <= instead of < in boundary check
- Assuming StopIteration triggers before error
- Ignoring index increment effects
You need to design an iterator for a complex data structure that contains nested lists of integers. Which approach best follows the Iterator pattern principles to allow clients to iterate over all integers seamlessly?
- Flatten the nested lists into a single list before iteration.
- Implement a recursive iterator that yields integers from nested lists on demand.
- Expose the internal nested list structure and let clients handle iteration.
- Provide separate iterators for each nested list and require clients to manage them.
Solution
Step 1: Understand Iterator pattern goal
The pattern aims to hide internal structure and provide a simple way to access elements sequentially.Step 2: Evaluate each approach
Flatten the nested lists into a single list before iteration flattens data upfront, which may be inefficient and breaks lazy access. Implement a recursive iterator that yields integers from nested lists on demand uses a recursive iterator to yield elements on demand, hiding complexity and supporting lazy iteration. Options C and D expose internal structure or complexity to clients, violating encapsulation.Final Answer:
Implement a recursive iterator that yields integers from nested lists on demand -> Option AQuick Check:
Recursive iterator hides structure, yields elements lazily [OK]
- Flattening data upfront losing lazy iteration benefits
- Exposing internal structure breaking encapsulation
- Forcing clients to manage multiple iterators
