Python:在字典中查找具有唯一值的键?

时间:2022-07-03 00:19:15

I receive a dictionary as input, and want to return a list of keys for which the dictionary values are unique in the scope of that dictionary.

我收到一个字典作为输入,并希望返回字典值在该字典范围内唯一的键列表。

I will clarify with an example. Say my input is dictionary a, constructed as follows:

我将以一个例子来澄清。说我的输入是字典a,构造如下:

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  

The result I expect is ['dog', 'snake'].

我期待的结果是['dog','snake']。

There are obvious brute force ways to achieve this, however I wondered if there's a neat Pythonian way to get the job done.

有明显的蛮力方法来实现这一点,但我想知道是否有一种巧妙的Python方式来完成工作。

9 个解决方案

#1


I think efficient way if dict is too large would be

我认为如果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]

#2


Here is a solution that only requires traversing the dict once:

这是一个只需要遍历一次dict的解决方案:

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)

#3


Note that this actually is a bruteforce:

请注意,这实际上是一个暴力:

l = a.values()
b = [x for x in a if l.count(a[x]) == 1]

#4


>>> 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']
>>>

#5


A little more verbose, but does need only one pass over a:

更冗长,但只需要一次通过:

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 ]

( I hope it works, since I can't test it here )

(我希望它有效,因为我不能在这里测试)

#6


What about subclassing?

子类化怎么样?

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']

#7


Here's another variation.

这是另一种变化。

>>> 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']

I'm partial to this because the inverted dictionary is such a common design pattern.

我偏爱这个因为倒置字典是一种常见的设计模式。

#8


You could do something like this (just count the number of occurrences for each value):

你可以做这样的事情(只计算每个值的出现次数):

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

#9


Use nested list comprehensions!

使用嵌套列表推导!

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]

#1


I think efficient way if dict is too large would be

我认为如果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]

#2


Here is a solution that only requires traversing the dict once:

这是一个只需要遍历一次dict的解决方案:

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)

#3


Note that this actually is a bruteforce:

请注意,这实际上是一个暴力:

l = a.values()
b = [x for x in a if l.count(a[x]) == 1]

#4


>>> 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']
>>>

#5


A little more verbose, but does need only one pass over a:

更冗长,但只需要一次通过:

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 ]

( I hope it works, since I can't test it here )

(我希望它有效,因为我不能在这里测试)

#6


What about subclassing?

子类化怎么样?

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']

#7


Here's another variation.

这是另一种变化。

>>> 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']

I'm partial to this because the inverted dictionary is such a common design pattern.

我偏爱这个因为倒置字典是一种常见的设计模式。

#8


You could do something like this (just count the number of occurrences for each value):

你可以做这样的事情(只计算每个值的出现次数):

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

#9


Use nested list comprehensions!

使用嵌套列表推导!

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]