Вопрос по dictionary, python – Python: поиск ключей с уникальными значениями в словаре?
Я получаю словарь в качестве входных данных и хочу вернуть список ключей, для которых значения словаря являются уникальными в области действия этого словаря.
Я уточню с примером. Скажем, мой ввод - словарь a, построенный следующим образом:
<code>a = dict() a['cat'] = 1 a['fish'] = 1 a['dog'] = 2 # <-- unique a['bat'] = 3 a['aardvark'] = 3 a['snake'] = 4 # <-- unique a['wallaby'] = 5 a['badger'] = 5 </code>
Результат, который я ожидаю['dog', 'snake']
.
Существуют очевидные способы грубой силы для достижения этой цели, однако мне было интересно, есть ли аккуратный Pythonian способ выполнить свою работу.
что эффективный способ, если dict слишком велик, был бы
countMap = {}
for v in a.itervalues():
countMap[v] = countMap.get(v,0) + 1
uni = [ k for k, v in a.iteritems() if countMap[v] == 1]
for k, v in a.iteritems():
но не использует k !!!
Вот решение, которое требует только одного раза:
def unique_values(d):
seen = {} # dict (value, key)
result = set() # keys with unique values
for k,v in d.iteritems():
if v in seen:
result.discard(seen[v])
else:
seen[v] = k
result.add(k)
return list(result)
result
... документы говорят "" "remove (elem) Удалить элемент elem из набора. Вызывает KeyError, если elem не содержится в наборе." ""
revDict = {}
for k, v in a.iteritems():
if v in revDict:
revDict[v] = None
else:
revDict[v] = k
[ x for x in revDict.itervalues() if x != None ]
(Надеюсь, это сработает, поскольку я не могу проверить это здесь)
x is not None
предпочтительнееx != None
.
>>> b = []
>>> import collections
>>> bag = collections.defaultdict(lambda: 0)
>>> for v in a.itervalues():
... bag[v] += 1
...
>>> b = [k for (k, v) in a.iteritems() if bag[v] == 1]
>>> b.sort() # optional
>>> print b
['dog', 'snake']
>>>
lambda: 0
более явный, чемint
... AFAICT, до наступления defaultdict [2.5] число людей, знавших, что int () выдает 0 [начиная с 2.2] вместо исключения, было <epsilon, а число тех, кто использовал эти знания, было еще меньше: -)
ждений для каждого значения):
def unique(a):
from collections import defaultdict
count = defaultdict(lambda: 0)
for k, v in a.iteritems():
count[v] += 1
for v, c in count.iteritems():
if c <= 1:
yield v
defaultdict(int)
быть немного яснее, чемdefaultdict(lambda:0)
. Так как по умолчанию dict почти любого другого типа будет просто использовать имя типа.
А как насчет подклассов?
class UniqueValuesDict(dict):
def __init__(self, *args):
dict.__init__(self, *args)
self._inverse = {}
def __setitem__(self, key, value):
if value in self.values():
if value in self._inverse:
del self._inverse[value]
else:
self._inverse[value] = key
dict.__setitem__(self, key, value)
def unique_values(self):
return self._inverse.values()
a = UniqueValuesDict()
a['cat'] = 1
a['fish'] = 1
a[None] = 1
a['duck'] = 1
a['dog'] = 2 # <-- unique
a['bat'] = 3
a['aardvark'] = 3
a['snake'] = 4 # <-- unique
a['wallaby'] = 5
a['badger'] = 5
assert a.unique_values() == ['dog', 'snake']
del a['bat']; print a.unique_values()
приведет кaardvark
появится в выводе, но, к сожалению, это не так, и исправление, которое потребует еще большего количества сверток и Double__underscores: -
Используйте вложенные списки!
print [v[0] for v in
dict([(v, [k for k in a.keys() if a[k] == v])
for v in set(a.values())]).values()
if len(v) == 1]
k in a
вместо тогоk in a.keys()
(2) Используйтеwhatever.itervalues()
вместо тогоwhatever.values()
(3) Часть dict (yadda yadda) создает уже перевернутую инверсиюa
неэффективно (4) Это ни аккуратно, ни Python (ic | ian) ... но это, конечно, не очевидно! (5) Подсчитайте количество респондентов, чьи первые попытки решить так называемую тривиальную проблему были сложными.
solution
можно редактировать (используя только клавишу удаления!), чтобы избавиться от построения обратного; все еще O (N ^ 2), хотя:print [v[0] for v in [[k for k in a if a[k] == v] for v in set(a.values())] if len(v) == 1]
Вот еще один вариант.
>>> import collections
>>> inverse= collections.defaultdict(list)
>>> for k,v in a.items():
... inverse[v].append(k)
...
>>> [ v[0] for v in inverse.values() if len(v) == 1 ]
['dog', 'snake']
Я неравнодушен к этому, потому что перевернутый словарь - такой распространенный шаблон проектирования.
[v[0] for v in inverse.itervalues() if len(v) == 1
(3) В любом случае построение перевернутого диктата излишне.