``````

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 [ ]:

``````