Konstantina K.
Konstantina K.

Reputation: 41

Find minimum non zero value in dictionary (Python)

I have a dictionary and I would like to get the key whose value is the minimum nonzero.

E.g. given the input:

{1:0, 2:1, 3:2}

It would return 2.

Upvotes: 4

Views: 3194

Answers (5)

vash_the_stampede
vash_the_stampede

Reputation: 4606

Solution

some_dict = {1:0, 2:1, 3:2}
compare = []

for k, v in some_dict.items():
    if k != 0:
        compare.append(k)

x = min(compare)

print(x)

I just appended all the non-zero keys to a list (compare) and then applied min(compare)

We can plug x back in and check that it is pointing to the key 1 which is the smallest non-zero key and that returns it's value which is 0

>>> print(some_dict[x]) 
>>> 0

Upvotes: 0

Jonas Wolff
Jonas Wolff

Reputation: 2244

print(min(i for i in dictionary if  dictionary[i] != 0))

this makes a set with no zeros and return the minimum value in that set. Though it is worth pointing out this makes 2 iterations and is thus slower than Maor Refaeli's solution.

Upvotes: 0

jpp
jpp

Reputation: 164643

You can use the fact 0 is considered False to filter out 0 values. Then use next with a generator expression:

d = {1:0, 2:1, 3:2}

val = min(filter(None, d.values()))
res = next(k for k, v in d.items() if v == val)  # 2

This will only return one key in the case of duplicate keys with 1 as value. For multiple matches, you can use a list comprehension:

res = [k for k, v in d.items() if v == val]

Note your literal ask for "minimum non-zero" will include negative values.

Performance note

The above solution is 2-pass but has time complexity O(n), it's not possible to have lower complexity than this. A 1-pass O(n) solution is possible as shown by @Maor, but this isn't necessarily more efficient:

# Python 3.6.0

%timeit jpp(d)  # 43.9 ms per loop
%timeit mao(d)  # 98.8 ms per loop
%timeit jon(d)  # 183 ms per loop
%timeit reu(d)  # 303 ms per loop

Code used for benchmarking:

from random import randint

n = 10**6
d = {i: randint(0, 9) for i in range(n)}

def jpp(d):
    val = min(filter(None, d.values()))
    return next(k for k, v in d.items() if v == val)

def mao(d):
    min_val = None
    result = None
    for k, v in d.items():
        if v and (min_val is None or v < min_val):
            min_val = v
            result = k
    return result

def jon(d):
    return min({i for i in d if d[i] != 0})

def reu(d):
    no_zeros = {k: v for k, v in d.items() if v != 0}
    key, val = min(no_zeros.items(), key=itemgetter(1))
    return key

Upvotes: 4

Maor Refaeli
Maor Refaeli

Reputation: 2517

You can do it on one iteration.

d = {1:0, 2:1, 3:2}

# Save the minimum value and the key that belongs to it as we go
min_val = None
result = None
for k, v in d.items():
    if v and (min_val is None or v < min_val):
        min_val = v
        result = k

print(result)

Some assumptions:

  • Negative values will be considered
  • It will return the first key that found
  • If it helps, min_val will hold the minimum value

Upvotes: 6

Reut Sharabani
Reut Sharabani

Reputation: 31339

Assuming the dict is named a:

from operator import itemgetter

a = {1:0, 2:1, 3:2}
# remove zeros
no_zeros = {k: v for k, v in a.items() if v != 0} # can use `if v`
# find minimal key and value (by value)
key, val = min(no_zeros.items(), key=itemgetter(1))
# key = 2, val = 1

Upvotes: 2

Related Questions