In [38]:
def multimax(iterable):
    """ Return a list of all maximum values """
    max_item = max(iterable)
    
    return [
        item
        for item in iterable
        if item == max_item
    ]

In [39]:
multimax([1, 2, 4, 3])


Out[39]:
[4]

In [40]:
multimax([1, 4, 2, 4, 3])


Out[40]:
[4, 4]

In [41]:
multimax([1, 1, 1])


Out[41]:
[1, 1, 1]

Bonus1: multimax function returns an empty list if the given iterable is empty


In [42]:
multimax([])


---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-42-27ec5c77234b> in <module>()
----> 1 multimax([])

<ipython-input-38-ea524e1d7f33> in multimax(iterable)
      1 def multimax(iterable):
      2     """ Return a list of all maximum values """
----> 3     max_item = max(iterable)
      4 
      5     return [

ValueError: max() arg is an empty sequence

In [43]:
def multimax(iterable):
    """ Return a list of all maximum values """
    try:
        max_item = max(iterable)
    except ValueError:
        return []
    
    return [
        item
        for item in iterable
        if item == max_item
    ]

In [44]:
multimax([])


Out[44]:
[]

In [45]:
def multimax(iterable):
    """ Return a list of all maximum values """
    max_item = max(iterable, default=None) # Using the default keyword-only argument of max prevents exception.
    
    return [
        item
        for item in iterable
        if item == max_item
    ]

In [46]:
multimax([])


Out[46]:
[]

Bonus2: multimax function will work with iterators (lazy iterables) such as files, zip objects, and generators


In [53]:
numbers = [1, 3, 8, 5, 4, 10, 6]
odds = (n for n in numbers if n % 2 == 1)

In [48]:
multimax(odds)


Out[48]:
[]

In [54]:
def multimax(iterable):
    """ Return a list of all maximum values """
    maximums = []
    
    for item in iterable:
        if not maximums or maximums[0] == item:
            maximums.append(item)
        else:
            if item > maximums[0]:
                maximums = [item]
                
    return maximums

In [50]:
multimax([])


Out[50]:
[]

In [51]:
multimax([1, 4, 2, 4, 3])


Out[51]:
[4, 4]

In [55]:
numbers = [1, 3, 8, 5, 4, 10, 6]
odds = (n for n in numbers if n % 2 == 1)

In [56]:
multimax(odds)


Out[56]:
[5]

Bonus3: multimax function accept a keyword argument called "key" that is a function which will be used to determine the key by which to compare values as maximums


In [73]:
def multimax(iterable, key=None):
    """ Return a list of all maximum values """
    if key is None:
        def key(item): return item
        
    maximums = []
    key_max = None
    
    for item in iterable:
        k = key(item)
        
        if k == key_max:
            maximums.append(item)
        elif not maximums or k > key_max:
            key_max = k
            maximums = [item]          
            
    return maximums

In [74]:
multimax([1, 2, 4, 3])


Out[74]:
[4]

In [75]:
multimax([1, 4, 2, 4, 3])


Out[75]:
[4, 4]

In [76]:
numbers = [1, 3, 8, 5, 4, 10, 6]
odds = (n for n in numbers if n % 2 == 1)

In [77]:
multimax(odds)


Out[77]:
[5]

In [78]:
multimax([])


Out[78]:
[]

In [79]:
words = ["cheese", "shop", "ministry", "of", "silly", "walks", "argument", "clinic"]

In [80]:
multimax(words, key=len)


Out[80]:
['ministry', 'argument']

We may use lambda when no key is provided like so:


In [87]:
def multimax(iterable, key=lambda x: x):
    """ Return a list of all maximum values """
    maximums = []
    key_max = None
    
    for item in iterable:
        k = key(item)
        
        if k == key_max:
            maximums.append(item)
        elif not maximums or k > key_max:
            key_max = k
            maximums = [item]          
            
    return maximums

Unit Tests


In [88]:
import unittest


class MultiMaxTests(unittest.TestCase):

    """Tests for multimax."""

    def test_single_max(self):
        self.assertEqual(multimax([1, 2, 4, 3]), [4])

    def test_two_max(self):
        self.assertEqual(multimax([1, 4, 2, 4, 3]), [4, 4])

    def test_all_max(self):
        self.assertEqual(multimax([1, 1, 1, 1, 1]), [1, 1, 1, 1, 1])

    def test_lists(self):
        inputs = [[0], [1], [], [0, 1], [1]]
        expected = [[1], [1]]
        self.assertEqual(multimax(inputs), expected)

    def test_order_maintained(self):
        inputs = [
            (3, 2),
            (2, 1),
            (3, 2),
            (2, 0),
            (3, 2),
        ]
        expected = [
            inputs[0],
            inputs[2],
            inputs[4],
        ]
        outputs = multimax(inputs)
        self.assertEqual(outputs, expected)
        self.assertIs(outputs[0], expected[0])
        self.assertIs(outputs[1], expected[1])
        self.assertIs(outputs[2], expected[2])

    # To test the Bonus part of this exercise, comment out the following line
    # @unittest.expectedFailure
    def test_empty(self):
        self.assertEqual(multimax([]), [])

    # To test the Bonus part of this exercise, comment out the following line
    # @unittest.expectedFailure
    def test_iterator(self):
        numbers = [1, 4, 2, 4, 3]
        squares = (n**2 for n in numbers)
        self.assertEqual(multimax(squares), [16, 16])

    # To test the Bonus part of this exercise, comment out the following line
    # @unittest.expectedFailure
    def test_key_function(self):
        words = ["alligator", "animal", "apple", "artichoke", "avalanche"]
        outputs = ["alligator", "artichoke", "avalanche"]
        self.assertEqual(multimax(words, key=len), outputs)


if __name__ == "__main__":
    unittest.main(argv=['first-arg-is-ignored'], exit=False)


........
----------------------------------------------------------------------
Ran 8 tests in 0.003s

OK

In [ ]: