Search code examples
pythonmaxmin

How to get min and max value of member in list in one pass?


I can use functools.reduce or min / max to get min and max of members in a list. But to get both in one pass I need to write a loop:

from functools import reduce

class foo:
    def __init__(self,value): self.value = value
    
x = []
x.append(foo(1))
x.append(foo(2))
x.append(foo(3))

min_value = reduce(lambda a,b: a if a.value < b.value else b,x).value
max_value = reduce(lambda a,b: a if a.value > b.value else b,x).value

print(min_value)
print(max_value)

min_value2 = min(x,key=lambda a: a.value).value
max_value2 = max(x,key=lambda a: a.value).value

print(min_value2)
print(max_value2)

min_value3 = x[0].value
max_value3 = x[0].value
for f in x:
    if f.value < min_value3: min_value3 = f.value
    if f.value > max_value3: max_value3 = f.value
    
print(min_value3)
print(max_value3)

Is it possible to get min and max in one pass without writing a plain loop?


Solution

  • You could use a tuple as your aggregator. Something like this maybe?

    min_value, max_value = reduce(lambda a, b: 
      (a[0] if a[0].value < b.value else b, a[1] if a[1].value > b.value else b), 
      x, 
      (x[0], x[1]))
    

    The output should be a tuple where the first is the minimum and the second the maximum.

    Example in the REPL, demonstrating that the objects requested are returned, and that the values are correct:

    >>> class Foo:
    ...     def __init__(self,value): self.value = value
    ...
    >>> ls = [Foo(1), Foo(2), Foo(3)]
    >>> min_value, max_value = reduce(lambda a, b: (a[0] if a[0].value < b.value else b, a[1] if a[1].value > b.value else b), ls, (ls[0], ls[1]))
    >>> min_value
    <__main__.Foo object at 0x10bd20940>
    >>> min_value.value
    1
    >>> max_value.value
    3
    

    For what it's worth, though, I think it's a little clearer if you use a helper function. In this way it's easier to think cleanly about what your accumulator is (your Tuple) and how you're doing the comparison and using reduce().

    from typing import Tuple
    from functools import reduce
    
    
    class Foo:
        def __init__(self, value): self.value = value
    
        def __repr__(self):
            return f"{self.value}"
    
    
    def min_max(accumulator: Tuple[Foo, Foo], element: Foo) -> Tuple[Foo, Foo]:
        minimum, maximum = accumulator
        return (minimum if minimum.value < element.value else element,
                maximum if maximum.value > element.value else element)
    
    
    ls = [Foo(x) for x in range(0, 4)]  # Or however you construct this list
    minimum, maximum = reduce(min_max, ls, (ls[0], ls[0]))
    print(f"{minimum=} {maximum=}")
    

    Yielding:

    minimum=0 maximum=3