Batu Lab NotesPractical developer guides

Use min and max together for a range summary

By Batu ยท English technical notes

Also published in our primary archive.

Use min and max together for a range summary

For a range summary of [4, 1, 9, 2], use one guarded pass when the input may be a one-shot iterator. The range_summary() function below returns {'min': 1, 'max': 9}; for an empty generator it returns None. This defines the boundary result in the function contract instead of relying on an exception from a later extrema call.

Calling min(stream) followed by max(stream) is not equivalent for a generator. min() consumes the supplied iterator while finding 1; when max() receives that same exhausted generator, it raises ValueError. The first result line captures that concrete failure. On a reusable list, two calls do calculate the extrema, but they still traverse the data separately. The meaningful distinction here is iterator consumption, not a claim that separate calls always produce an incorrect answer.

The correction obtains the first item once, returning None when no item exists. It then compares each remaining item with the current low and high values in one loop. The source list remains unchanged because iter(values) reads it without sorting or mutating it. Assertions cover this numeric fixture, the one-shot failure, and the empty boundary; they do not establish behavior for values that cannot be compared with < and >.

No newer API is required: this example works on Python 3.0+. Python's min() documentation and its max() documentation specify that an empty iterable without a default raises ValueError.

AI assistance disclosure: This article was prepared with AI assistance and checked with the synthetic assertions shown below.

values = [4, 1, 9, 2]

def two_extrema_from_one_shot(items):
    low = min(items)
    try:
        high = max(items)
    except ValueError:
        return {"min": low, "max_error": "ValueError"}
    return {"min": low, "max": high}

def range_summary(items):
    iterator = iter(items)
    try:
        first = next(iterator)
    except StopIteration:
        return None

    low = high = first
    for value in iterator:
        if value < low:
            low = value
        if value > high:
            high = value
    return {"min": low, "max": high}

failed_summary = two_extrema_from_one_shot(iter(values))
summary = range_summary(iter(values))
empty_summary = range_summary(iter(()))

assert values == [4, 1, 9, 2]
assert failed_summary == {"min": 1, "max_error": "ValueError"}
assert summary == {"min": 1, "max": 9}
assert empty_summary is None

print("input unchanged:", values)
print("two extrema on one-shot iterator:", failed_summary)
print("one-pass summary:", summary)
print("empty-generator summary:", empty_summary)
input unchanged: [4, 1, 9, 2]
two extrema on one-shot iterator: {'min': 1, 'max_error': 'ValueError'}
one-pass summary: {'min': 1, 'max': 9}
empty-generator summary: None