# Allow \`min()\` and \`max()\` to return both the key and value

**URL:** <https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990>\
**Category:** Ideas\
**Tags:** stdlib\
**Created:** [September 5, 2024, 10:40am UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990 "2024-09-05T10:40:57Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![EwoutH](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ewouth/32/9371_2.png) [@EwoutH](https://discuss.python.org/u/EwoutH)\
**Post date:** [September 5, 2024, 10:40am UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/1 "2024-09-05T10:40:57Z")

</div>

### Problem

Currently, `min()` and `max()` return only the minimum/maximum value, or in cases where the input is a mapping (like a dictionary), only the key. There are many situations where both the value and the associated key or object are needed. This leads to redundant code where users have to call the function twice or perform extra lookups.

```python
data = {'a': 5, 'b': 2, 'c': 7, 'd': 1}

# Current behavior
min_key = min(data, key=data.get) # 'd'
min_value = data[min_key] # 1

```

### Solution

Introduce a `return_both` keyword argument to `min()` and `max()`. When set to `True`, the function returns a tuple containing both the key (or element) and the value used for comparison.

```python
# Proposed behavior with return_both=True
min_key, min_value = min(data, key=data.get, return_both=True) # ('d', 1)

```

### Benefits

- Simplifies code when both the element and the comparison value are needed.
- Reduces the need for extra lookups or redundant calls to `min()` and `max()`.
- Potentially increases performance due to an eliminated dict lookup.

### Backward Compatibility

The default behavior remains unchanged. Only when `return_both=True` is explicitly set will a tuple be returned.

_I’m open to suggestions for naming for the `return_both` keyword argument._

---

<div class="post-metadata">

**Author:** ![massover](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/massover/32/8099_2.png) [@massover](https://discuss.python.org/u/massover)\
**Post date:** [September 5, 2024, 10:51am UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/2 "2024-09-05T10:51:53Z")

</div>

```python
>>> data = {'a': 5, 'b': 2, 'c': 7, 'd': 1}
>>> list(data)
['a', 'b', 'c', 'd']
>>> min(data)
'a'
>>> key, value = min(data.items(), key=lambda x: x[0])
>>> key
'a'
>>> value
5

```

`min` returns the minimum of an iterable. by default a dictionary will iterate over it’s keys, and thats why it picks the min of the keys, not values. if you want to return something else, pass in a different iterable like `dict.items()`, and then use the flexible [key](https://docs.python.org/3/library/functions.html#min) argument.

---

<div class="post-metadata">

**Author:** ![MegaIng](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/megaing/32/16162_2.png) [@MegaIng](https://discuss.python.org/u/MegaIng)\
**Post date:** [September 5, 2024, 10:55am UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/3 "2024-09-05T10:55:12Z")

</div>

See [Add `index: bool` keyword to `min`, `max`, `sorted`](https://discuss.python.org/t/add-index-bool-keyword-to-min-max-sorted/60257)

---

<div class="post-metadata">

**Author:** ![brass75](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/brass75/32/19784_2.png) [@brass75](https://discuss.python.org/u/brass75)\
**Post date:** [September 5, 2024, 1:12pm UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/4 "2024-09-05T13:12:57Z")

</div>

```python
>>> data = {'a': 5, 'b': 2, 'c': 7, 'd': 1}
>>> min(data.items())
('a', 5)

```

If you use `min` or `max` on the `items()` of a dictionary you get the key value pair.  
You can also use the `key` parameter in `min` or `max` to compare the values as opposed to the keys:

```python
>>> min(data.items(), key=lambda x: x[1])
('d', 1)

```

This functionality exists already although you do have to know how to do it.

---

<div class="post-metadata">

**Author:** ![EwoutH](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ewouth/32/9371_2.png) [@EwoutH](https://discuss.python.org/u/EwoutH)\
**Post date:** [September 6, 2024, 10:34am UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/5 "2024-09-06T10:34:57Z")

</div>

Thanks everyone! I was thought I couldn’t be the first person with the problem, considering how general it’s. Sorry for the noise.

Thanks for the replies, it’s appreciated!

Edit: Maybe this snippet with a short explanation can be added somewhere in the docstring? What would be a fitting place, if any (considering it’s generalizable to `min`, `max`, `sorted`, and probably more)?

```Python
min(data.items(), key=lambda x: x[1])

```

---

<div class="post-metadata">

**Author:** ![Wombat](https://avatars.discourse-cdn.com/v4/letter/w/e480ec/32.png) [@Wombat](https://discuss.python.org/u/Wombat)\
**Post date:** [September 10, 2024, 7:17am UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/6 "2024-09-10T07:17:33Z")

</div>

> [@EwoutH](#):
>
> Maybe this snippet with a short explanation can be added somewhere in the docstring?  
> `min(data.items(), key=lambda x: x[1])`

Your baseline solution was better in almost every way:

```python
data = {'a': 5, 'b': 2, 'c': 7, 'd': 1}
min_key = min(data, key=data.get)
min_value = data[min_key]

```

---

<div class="post-metadata">

**Author:** ![chepner](https://avatars.discourse-cdn.com/v4/letter/c/22d042/32.png) [@chepner](https://discuss.python.org/u/chepner)\
**Post date:** [September 10, 2024, 1:07pm UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/7 "2024-09-10T13:07:41Z")

</div>

> [@Wombat](#):
>
> Your baseline solution was better in almost every way:
> 
> ```python
> data = {'a': 5, 'b': 2, 'c': 7, 'd': 1}
> min_key = min(data, key=data.get)
> min_value = data[min_key]
> 
> ```

Note really. This _allows_ (no matter how hard you would need to try to mess it up) for `min_value` to be assigned a value other than `data[min_key]`. Calling `min` on `data.items()` does not, as there is no opportunity to index `data` independently.

```
min_key, min_value = min(data.items(), key=lambda x: x[1])

```

---

<div class="post-metadata">

**Author:** ![Stefan2](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/stefan2/32/18492_2.png) [@Stefan2](https://discuss.python.org/u/Stefan2)\
**Post date:** [September 10, 2024, 1:58pm UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/8 "2024-09-10T13:58:44Z")

</div>

> [@chepner](#):
>
> This _allows_ […] for `min_value` to be assigned a value other than `data[min_key]`.

How so? It literally ends with `min_value = data[min_key]`.

---

<div class="post-metadata">

**Author:** ![chepner](https://avatars.discourse-cdn.com/v4/letter/c/22d042/32.png) [@chepner](https://discuss.python.org/u/chepner)\
**Post date:** [September 10, 2024, 2:02pm UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/9 "2024-09-10T14:02:07Z")

</div>

> [@Stefan2](#):
>
> How so? It literally ends with `min_value = data[min_key]`.

`data.items()` returns tuples of _true_ key/value pairs. `min_value = data[min_key]` relies on the programmer to use the correct variable as the index.

Suppose I decided to rename `min_key` to `smallest_key`. Using `min(data.items(), ...)`, I only have to make that change in one place; `min_key = ...; min_value = data[min_key]`, there are two places to make that change.

---

<div class="post-metadata">

**Author:** ![Stefan2](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/stefan2/32/18492_2.png) [@Stefan2](https://discuss.python.org/u/Stefan2)\
**Post date:** [September 10, 2024, 2:14pm UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/10 "2024-09-10T14:14:38Z")

</div>

Ok but that’s different. Anyway, Zeke did say “ **almost** every way”.

Another, with only one `min_key` (but it’s somewhat hidden):

```python
data = {'a': 5, 'b': 2, 'c': 7, 'd': 1}
min_value = data[min_key := min(data, key=data.get)]

```

---

<div class="post-metadata">

**Author:** ![blhsing](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/blhsing/32/25812_2.png) [@blhsing](https://discuss.python.org/u/blhsing)\
**Post date:** [September 11, 2024, 1:09am UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/11 "2024-09-11T01:09:24Z")

</div>

> [@Wombat](#):
>
> Your baseline solution was better in almost every way:
> 
> ```python
> data = {'a': 5, 'b': 2, 'c': 7, 'd': 1}
> min_key = min(data, key=data.get)
> min_value = data[min_key]
> 
> ```

One way how this is better is that it avoids the performance overhead of creating new objects while `data.items()` has to create a new tuple for each entry in the dict.

```python
from timeit import timeit

def stefan2():
    min_value = data[min_key := min(data, key=data.get)]

def brass75():
    key, value = min(data.items(), key=lambda x: x[1])

data = {'a': 5, 'b': 2, 'c': 7, 'd': 1}
print(timeit(stefan2, globals=globals())) # outputs 0.308526000007987
print(timeit(brass75, globals=globals())) # outputs 0.5086688000010327

```

---

<div class="post-metadata">

**Author:** ![Stefan2](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/stefan2/32/18492_2.png) [@Stefan2](https://discuss.python.org/u/Stefan2)\
**Post date:** [September 11, 2024, 4:49am UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/12 "2024-09-11T04:49:50Z")

</div>

> [@blhsing](#):
>
> avoids the performance overhead of creating new objects while `data.items()` has to create a new tuple for each entry in the dict.

I think it only creates a new tuple object for each entry if the values are strictly decreasing, otherwise it reuses the tuple. But filling the tuple and running the lambda function still costs.

---

<div class="post-metadata">

**Author:** ![blhsing](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/blhsing/32/25812_2.png) [@blhsing](https://discuss.python.org/u/blhsing)\
**Post date:** [September 11, 2024, 5:34am UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/13 "2024-09-11T05:34:26Z")

</div>

> [@Stefan2](#):
>
> I think it only creates a new tuple object for each entry if the values are strictly decreasing, otherwise it reuses the tuple.

Sorry but I don’t get what you’re saying here. `dict.items` [creates tuples](https://github.com/python/cpython/blob/00ffdf27367fb9aef247644a96f1a9ffb5be1efe/Objects/dictobject.c#L3405) unconditionally for all dict entries, which `min` [iterates](https://github.com/python/cpython/blob/00ffdf27367fb9aef247644a96f1a9ffb5be1efe/Python/bltinmodule.c#L1846) over unconditionally.

> [@](#):
>
> But filling the tuple and running the lambda function still costs.

The slow bytecode execution of a lambda function is indeed also what slows down the latter code.

The gap closes significantly if it is switched to the C-implemented `operator.itemgetter` instead:

```python
from timeit import timeit
from operator import itemgetter

def stefan2():
    min_value = data[min_key := min(data, key=data.get)]

def brass75():
    key, value = min(data.items(), key=itemgetter(1))

data = {'a': 5, 'b': 2, 'c': 7, 'd': 1}
print(timeit(stefan2, globals=globals())) # outputs 0.2945460000773892
print(timeit(brass75, globals=globals())) # outputs 0.3461032999912277

```

---

<div class="post-metadata">

**Author:** ![Stefan2](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/stefan2/32/18492_2.png) [@Stefan2](https://discuss.python.org/u/Stefan2)\
**Post date:** [September 11, 2024, 6:09am UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/14 "2024-09-11T06:09:55Z")

</div>

> [@blhsing](#):
>
> `dict.items` [creates tuples](https://github.com/python/cpython/blob/00ffdf27367fb9aef247644a96f1a9ffb5be1efe/Objects/dictobject.c#L3405) unconditionally

That’s not the items view/iterator, that’s building a list of all items at once.

---

<div class="post-metadata">

**Author:** ![blhsing](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/blhsing/32/25812_2.png) [@blhsing](https://discuss.python.org/u/blhsing)\
**Post date:** [September 11, 2024, 6:25am UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/15 "2024-09-11T06:25:06Z")

</div>

Ah I see the [condition](https://github.com/python/cpython/blob/00ffdf27367fb9aef247644a96f1a9ffb5be1efe/Objects/dictobject.c#L5490) now. Thanks.

---

<div class="post-metadata">

**Author:** ![Stefan2](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/stefan2/32/18492_2.png) [@Stefan2](https://discuss.python.org/u/Stefan2)\
**Post date:** [September 11, 2024, 6:33am UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/16 "2024-09-11T06:33:09Z")

</div>

Oh and we’re talking about per-element costs, so better measure with more than just four elements. For example:

```python
data = {random(): random() for _ in range(10000)}
print(timeit(stefan2, globals=globals(), number=1000))
print(timeit(brass75, globals=globals(), number=1000))

```

[Attempt This Online!](https://ato.pxeger.com/run?1=hVHNSgQxDMZrnyLHFsbFFURZ6JOILB0mHYvTdmgzwiKefAwve9GH8mlMbYt4spf8fPm-JM3753qixxjO54-N7OXd18WbTdEDOY-OwPk1JmqR-EHiislQTB1zhH5GIkwVTyZMbBpaIyEmtJAJrQnXUh0E8PMuHJ_NsiFomAyZ-5J4whMcdMFkyQ3ACV28HfdQD1VoTCbn25suxCUDdKXO3JW5slRV4XdIuVeKVbiCi1_qeCwE3QPLqx3BhZKZUe6v-KlXsSYXSNaPkG2TAeYljmbJutnSLmx-xKQLjzv9obW5_6fVW7ST9NN8Aw)

I actually get _slower_ times for the 2-steps solution when compared to the `itemgetter` version (but a little _faster_ compared to the `lambda` version).

---

<div class="post-metadata">

**Author:** ![blhsing](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/blhsing/32/25812_2.png) [@blhsing](https://discuss.python.org/u/blhsing)\
**Post date:** [September 11, 2024, 6:39am UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/17 "2024-09-11T06:39:15Z")

</div>

Good point. That has to be caused by the overhead of hashing every key by calling `dict.get` for all dict entries. With `dict.items` it’s a direct dump with no hashing needed.

---

<div class="post-metadata">

**Author:** ![Stefan2](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/stefan2/32/18492_2.png) [@Stefan2](https://discuss.python.org/u/Stefan2)\
**Post date:** [September 11, 2024, 7:06am UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/18 "2024-09-11T07:06:18Z")

</div>

One more thing about “`stefan2`”: I just walrussed the existing solution. Here’s something actually Stefan-ish 🙂

```python
from operator import indexOf
from itertools import islice

def stefan2():
    values = data.values()
    value = min(values)
    index = indexOf(values, value)
    key = next(islice(data, index, None))

```

Seems a bit _faster_ on average than the `itemgetter` solution on my test data (but the `indexOf` search can make it slower when the values aren’t just floats but objects that check equality more slowly).

(I actually offered that [for statistics.mode](https://bugs.python.org/issue45851) once.)

---

<div class="post-metadata">

**Author:** ![blhsing](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/blhsing/32/25812_2.png) [@blhsing](https://discuss.python.org/u/blhsing)\
**Post date:** [September 11, 2024, 8:27am UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/19 "2024-09-11T08:27:54Z")

</div>

That’s quite clever indeed. Whether it’s faster than the `itemgetter` solution depends on the index where the minimum value is found. If it’s near the start it gets to save a lot of time by short-circuiting. On average your solution is indeed faster when the minimum value [occurs in the middle](https://ato.pxeger.com/run?1=hZJBTsQwDEUllt1yAS9TFFCLNAKN1CvAGVLqDhFtUiUumhHiJGxmA4fiNDh1ymgWiG4i-_n7O24-vqYDPXt3PH7O1F_ff19c9sGPQHZES2DHyQfKUbEQP2Ew5MPKLOG4QyIMwoNxHR-ZSvSH0nW4f-wFcptA3g_xl8bBPmFRdNhDJOyNu1XltgD-Xs0wY4QGOkPmRiJVnhCT0TolQPKLF-ezZ2Za6qXkBQ9c4HBPSrxVaq9FouHBOyxLmacNJsa7zToPK_WZ8zJXWgyPpRNuTltS9dKFK7j4zW7BQp92wj6grnhfO1S1hk1V1aytNORcSmioq5Qv34spWEdKfozK-9GwG3xrhtjkM7m7eWwxNEnIxmeyfI3_ZfI28hNZn8oP).

Relying on the index of the value to retrieve the key means that it is highly thread-unsafe though, unlike `dict.items`, which guarantees the correctness of every key-value mapping it yields.

---

<div class="post-metadata">

**Author:** ![Stefan2](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/stefan2/32/18492_2.png) [@Stefan2](https://discuss.python.org/u/Stefan2)\
**Post date:** [September 11, 2024, 1:23pm UTC](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990/20 "2024-09-11T13:23:03Z")

</div>

> [@blhsing](#):
>
> when the minimum value [occurs in the middle](https://ato.pxeger.com/run?1=hZJBTsQwDEUllt1yAS9TFFCLNAKN1CvAGVLqDhFtUiUumhHiJGxmA4fiNDh1ymgWiG4i-_n7O24-vqYDPXt3PH7O1F_ff19c9sGPQHZES2DHyQfKUbEQP2Ew5MPKLOG4QyIMwoNxHR-ZSvSH0nW4f-wFcptA3g_xl8bBPmFRdNhDJOyNu1XltgD-Xs0wY4QGOkPmRiJVnhCT0TolQPKLF-ezZ2Za6qXkBQ9c4HBPSrxVaq9FouHBOyxLmacNJsa7zToPK_WZ8zJXWgyPpRNuTltS9dKFK7j4zW7BQp92wj6grnhfO1S1hk1V1aytNORcSmioq5Qv34spWEdKfozK-9GwG3xrhtjkM7m7eWwxNEnIxmeyfI3_ZfI28hNZn8oP).

Yes, middle is also what I did when I said “on average”. More elaborate now, with 20 different positions all over the range:

```python
   291 ± 1 ms selfmade
   371 ± 1 ms value_then_key
   486 ± 7 ms items_itemgetter
   777 ± 9 ms key_then_value
   994 ± 9 ms items_lambda

Python: 3.12.2 (main, Jun 12 2024, 09:13:57) [GCC 14.1.1 20240522]

```

(`selfmade` is new, doesn’t use the `min` function)

> **Code**
>
> ```python
> from timeit import timeit
> from operator import itemgetter, indexOf
> from itertools import islice
> from random import random, shuffle
> from statistics import mean, stdev
> import sys
> 
> def key_then_value():
> value = data[key := min(data, key=data.get)]
> return key, value
> 
> def items_lambda():
> key, value = min(data.items(), key=lambda x: x[1])
> return key, value
> 
> def items_itemgetter():
> key, value = min(data.items(), key=itemgetter(1))
> return key, value
> 
> def value_then_key():
> values = data.values()
> value = min(values)
> index = indexOf(values, value)
> key = next(islice(data, index, None))
> return key, value
> 
> def selfmade():
> it = iter(data.items())
> key, value = next(it)
> for k, v in it:
> if v < value:
> key = k
> value = v
> return key, value
> 
> funcs = [key_then_value, items_lambda, items_itemgetter, value_then_key, selfmade]
> 
> data = {random(): random() for _ in range(10000)}
> 
> for f in funcs:
> print(f())
> 
> keys = list(data)[250::500]
> def run():
> for key in keys:
> value = data[key]
> data[key] = -1.0
> f()
> data[key] = value
> 
> times = {f: [] for f in funcs}
> def stats(f):
> ts = [t * 1e6 for t in sorted(times[f])[:5]]
> return f'{mean(ts):6.0f} ± {stdev(ts):2.0f} ms '
> for _ in range(100):
> shuffle(funcs)
> for f in funcs:
> t = timeit(run, globals=globals(), number=1) / 1
> times[f].append(t / len(keys))
> for f in sorted(funcs, key=stats):
> print(stats(f), f. __name__ )
> 
> print('\nPython:', sys.version)
> 
> ```
> 
> [Attempt This Online!](https://ato.pxeger.com/run?1=lVXNbpwwED61B55ibosrQpdIG1Wo-wpt7xQhJ9hZFDAr26x2tcqT9JJLe-8r9DH6NB2PDYSNmqpcwPPN7zfj4duP_cnuevX09H2w8urD77dvpO47sE0nGgtNt--1DaeIkH4vNLe9HrHGiu5eWCt0Ao2qxfGz9IoIaNv3rZk0TdvcCQ9qrmqn4xF_SsDsBinboGIst42xzd3koBNcoZKtxSEKInMyUVQLCQ_iVNmdUNWBt4OIWR4BPnSALdTc8gJVIN9C16jYnRNns3VfKebPSjLQwg5aOSTxxt67K9JULe9uaz76nnVgdpqSZsy8c28AxxyORVayf0eYyfyPKM-MMvZqEPr0NCG0IMkEllJ_itmCPxfYA15OjUZ5aHjAQiw2Jo4KShxt7BsfSCeTBD71SryerBGt7Hg99RLHcUtDtaCAvWTJx7QekTipD4hhXDT2nsgbsgEfvc0snRN_WMhG14e_JSwHdecoLJZzmCwmJ3nR5eSiJclUdYkkYJno8uxvB9IA4xdVVbmSUHIv4myND3vENFAunZzy8XXtdaNsLB1VEYZwWbZ4r4hFVlxv1nm-Wa9L4lwPaqSbiEMqGirUzBxd3qlyQiYJoldZup4AGcbpUimQ59aLS-sscyhKWBbx6IcBl4GJZcjNEtUW3kEmbkjfOn2DG0HUMbkrZMmKfFMurrVcnd0Oia1h-U26lo_w6yecaaGQ7JpknYFV9JLhEDvsqJiSm2fsknVKE7P0qzNGYhO4b_tb3ppteLv7q4buVuhtxuA9ZLNhqCDl-71QWBGirVCxawS2cYoXCqawfhcQT-x540fmEpBpVSneiarCSfDg6qv6Qvs_XyVul6YHoU3TK-b_BuGnMP4c_gA)

[Next page](https://discuss.python.org/t/allow-min-and-max-to-return-both-the-key-and-value/62990.md?page=2)
