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

Python:在字典中查找具有唯一值的键?,python,dictionary,Python,Dictionary,我收到一个字典作为输入,并希望返回一个键列表,其中字典值在该字典的范围内是唯一的 我将举例说明。假设我的输入是字典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'] =

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

我将举例说明。假设我的输入是字典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  
a=dict()
a['cat']=1
a[“鱼”]=1

注意,这实际上是一种蛮力:

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

您可以这样做(只需计算每个值的出现次数):

def唯一(a):
从集合导入defaultdict
count=defaultdict(lambda:0)
对于a.iteritems()中的k,v:
计数[v]+=1
对于v,c在计数中。iteritems():

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

使用嵌套列表理解

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]

这是另一个变体

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

我倾向于此,因为反转字典是一种常见的设计模式。

这里有一个解决方案,只需要遍历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)

更详细一点,但只需要通过一次:

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 ]

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

子类化怎么样

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']
类唯一值dict(dict):
定义初始化(self,*args):
dict.uuu init_uuu(self,*args)
self._倒数={}
定义设置项(自身、键、值):
如果self.values()中的值为:
如果值为自反:
del self.\u逆[值]
其他:
self.\u逆[值]=键
dict.\uuuuu setitem\uuuuuuuu(自身、键、值)
def唯一_值(自身):
返回self.\u inverse.values()
a=唯一值DICT()
a['cat']=1
a[“鱼”]=1
a[无]=1
a['duck']=1

a['dog']=2#它不会输出['dog','snake']l.count('dog')不是零吗?l在我的系统上是[3,3,2,1,4,5,1,5]。好的,我看到cobbal已经更正了代码。谢谢。这会产生值(2,4),而它应该产生键('dog','snake')。我发现
defaultdict(int)
defaultdict(lambda:0)
更清晰。因为几乎任何其他类型的默认dict都只会使用类型名称。如果一个值出现3次,您将尝试从
结果中删除一个不存在的元素。。。文档说“删除(elem)”从集合中删除元素elem。如果元素不包含在集合中,则引发KeyError。“”没错!我已更正为使用discard()。您希望最后一行中的[v[0]代表k,v…]按请求获取['dog','snake'。(1)使用.iterms()代替.items(),而不是.items()。(2) 最后一行不必要地提取密钥;如果len(v)=1,则应为
[v[0]。
(3)在任何情况下,构建整个反向dict都是多余的。如果其中一个字典键为None,则不起作用。例如,如果a为{None:1},则输出应为[None],但上述代码将生成[]。另外:
x不是None
x!=None
更可取。感谢您的评论!您完全正确。在实践中,很少使用None…但即使这样,也可以创建一些DummyObject:“Dummy=object()”不要使用None。我不认为以这种方式使用列表理解是一种胜利。对我来说,这只会使解决方案更难理解(没有双关语)。可读性是关键,这个解决方案在我看来不是那么可读。Rax要求“一种简洁的Python方法来完成工作”,而不是“显而易见”一个其他琐碎问题的解决方案。(1)在a
中使用
k而不是在a.keys()中使用
k
(2)使用
whater.itervalues()
而不是
whater.values()
(3)dict(yadda-yadda)部分正在低效地构建
a
的本已过度使用的逆函数(4)它既不整洁,也不是Python(ic | ian)…但这肯定不是显而易见的!(5)统计那些第一次尝试解决所谓的琐碎问题的人的数量。这个
解决方案
可以编辑(仅使用delete键!)以避免构建逆运算;仍然是O(N^2),尽管:
打印[v[0]表示v in[[k表示k in a,如果[k]==v]表示v in set(a.values())]如果len(v)==1]
这具有内存占用较小的优点,但最终会执行O(N)每次设置一个项目时都要进行搜索,因此它可能比字典制表法慢得多。此外,我认为你可以使用一个集合来代替dict。另一个问题:OP没有限制dict的内容是如何获得的。因此,人们会期望
dela['bat'];print a.unique_values()
将导致输出中出现
aardvark
,但遗憾的是,它没有出现,而修复该问题将需要更多的卷积和双下划线:-(使用集合会更美观。defaultdict(int),是的,但我会留下它,这样人们就会知道我们在没有defaultdicts@Ryan:True,但是
lambda:0
int
…更明确。在defaultdict到达[2.5]之前,知道int()产生0的人数[自2.2以来]而不是一个例外是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 ]
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']