Как определить наименьшее уникальное значение в словаре?

Ещё только учусь. Так что сильно не ругайте. Пытаюсь разобраться со словарями.
Поставил себе такую задачку:
Словарь: "имя": значение (штук 20)
Значения могут повторятся
Как определить наименьшее уникальное значение в словаре?
Например:
коля=23, света=26, настя=45, саня=23
Ожидаемый результат => света=26

Пока у меня получилось:
a = "roma"
b = "nastia"
c = "sasha"

aa = 12
bb = 23
cc = 45

qw = {a:aa, b:bb, c:cc}

if qw[a] < qw[b] and qw[a] < qw[c]:
    print a, qw.get(a)
elif qw[a] == qw[b] or qw[a] == qw[c] or qw[b] == qw[c]:
    print("нет наименьшего")
elif qw[b] < qw[a] and qw[b] < qw[c]:
    print b, qw.get(b)
else:
    print c, qw.get(c)


А теперь понимаю что если добавить в словарь ещё 10-20 значений будет полная Ж. Да и проверка на уникальность - как?
  • Вопрос задан
  • 4034 просмотра
Решения вопроса 1
adugin
@adugin Куратор тега Python
Самое простое и эффективное решение без лишних действий:
from collections import Counter

def min_unique(dictionary):
    minval, result = float('inf'), None
    counter = Counter(dictionary.itervalues())
    for key, val in dictionary.iteritems():
        if (val < minval) and (counter[val] == 1):
            minval = val
            result = (key, val)
    return result

data = {'a': 23, 'b': 26, 'c': 45, 'd': 23}
print min_unique(data)

Или так:
from operator import itemgetter
from itertools import ifilter

def min_unique(dictionary):
    inverted = dict()
    for key, value in dictionary.iteritems():
        inverted[value] = None if value in inverted else key
    filtered = ifilter(itemgetter(1), inverted.iteritems())
    try:
        value, key = min(filtered, key=itemgetter(0))
    except ValueError:
        value, key = None, None
    return key, value

Ещё интересный вариант с итераторами (узкое место - сортировка):
from operator import itemgetter
from itertools import groupby

def min_unique(dictionary, ig=itemgetter(1)):
    grouped = groupby(sorted(dictionary.viewitems(), key=ig), key=ig)
    for key, entries in grouped:
        entry = entries.next()
        try:
            entries.next()
        except StopIteration:
            return entry
        else:
            continue
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
iximiuz
@iximiuz
Необходимо стараться написать общий алгоритм решения задачи, а не перебрать допустимые варианты. Это можно делать даже без использования конкретного языка программирования, пользуясь лишь логическими утверждениями. Например: для начала подсчитаем количество вхождений каждого из значений в словаре (см. counter в коде). Затем выберем наименьшее уникальное значение. Для этого необходимо отфильтровать массив уникальных, затем отсортировать его по возрастанию и взять первый элемент (см. lowest_unique = (sorted([k for k in counter if counter[k] == 1]) or [None])[0]). Теперь необходимо найти ключ в начальном словаре, соответствующий найденному минимальному значению. Можно, конечно, перебрать все ключи в словаре и проверить их значения, но это долго. Если же сделать отражение начального словаря (переворот, flip), то заданное значение ключа можно будет найти простейшим обращением flipped_dict[lowest_unique]. Делаем переворот: flipped_dict = dict(zip(initial_dict.values(), initial_dict.keys())).

Полная версия кода:
initial_dict = {'Foo': 23, 'Bar': 26, 'Baz': 45, 'Bang': 23}
flipped_dict = dict(zip(initial_dict.values(), initial_dict.keys()))  
# print(flipped_dict)
# {26: 'Bar', 45: 'Baz', 23: 'Foo'}

counter = {}
for k in initial_dict:
	val = initial_dict[k]
	counter[val] = counter.get(val, 0) + 1
 # print(counter)  
 # {26: 1, 45: 1, 23: 2}

lowest_unique = (sorted([k for k in counter if counter[k] == 1]) or [None])[0]
if lowest_unique is None:
	print("Not found!")
else:
	print("Found [%s]->[%s]" % (flipped_dict[lowest_unique], lowest_unique))
Ответ написан
@Quirel
Не претендую на идеальность решения и максимальную скорость выполнения программы, я сам новичек.
d = {'a': 21, 'b': 5, 'c': 7, 'd': 5, 'e': 10}
u = {k: v for k, v in d.items() if tuple(d.values()).count(v) < 2}
print(min(u.items(), key=lambda i: i[1])[0])

Некоторые пояснения:
1. d - данный словарь
2. u - сформированный словрь, в котором остались только элементы с уникальными значениями
3. С помощью функции min вычисляем элемент путем сравнения значения значений (не ключей), получаем кортеж вида (key, value) и возвращаем и возвращаем [0] элемент, т.е. key.
---
Если использовать python2.7, тогда вместо u.itеmes(), лучше использовать u.iteritems()
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы