2009-06-23 5 views
14

मुझे इनपुट के रूप में एक शब्दकोश प्राप्त होता है, और उस कुंजी की एक सूची वापस करना चाहता है जिसके लिए शब्दकोश के दायरे में शब्दकोश मान अद्वितीय हैं।पायथन: एक शब्दकोश में अद्वितीय मूल्यों के साथ कुंजी खोजना?

मैं एक उदाहरण के साथ स्पष्टीकरण दूंगा। कहो मेरी इनपुट के रूप में इस शब्दकोश एक का निर्माण किया है:

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 

परिणाम मैं उम्मीद ['dog', 'snake'] है।

यह हासिल करने के लिए स्पष्ट ब्रूट फोर्स तरीके हैं, हालांकि मुझे आश्चर्य हुआ कि क्या काम पूरा करने के लिए एक साफ पाइथोनियन तरीका है।

उत्तर

12

मुझे लगता है कि कारगर तरीका अगर 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] 
+1

यह संग्रह .defaultdict (int), आईएमओ –

+0

हां के साथ सुंदर होगा, लेकिन मैं इसे छोड़ दूंगा ताकि लोगों को पता चले कि हम क्या करने के लिए उपयोग करते हैं जब कोई डिफ़ॉल्ट डिस्प्ले नहीं था –

+0

WASTEFUL: k के लिए v, v.iteritems में v): 'लेकिन इसका उपयोग नहीं करता !!! –

5

ध्यान दें कि यह वास्तव में एक bruteforce है:

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

यह उत्पादन नहीं [ 'कुत्ता', 'साँप'] –

+0

l.count नहीं है जाएगा ('कुत्ता') शून्य? एल [3, 3, 2, 1, 4, 5, 1, 5] मेरे सिस्टम पर है। –

+0

ठीक है, मुझे लगता है कि कोबबल ने कोड को पहले से ही सही कर दिया है। धन्यवाद। –

-1

आप कुछ इस तरह कर सकता है (बस प्रत्येक मान के लिए घटनाओं की संख्या गिनती):

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 
+0

यह मूल्य (2, 4) उत्पन्न कर रहा है जब इसे कुंजी ('कुत्ता', 'सांप') पैदा करना चाहिए। –

+1

मुझे 'डिफॉल्टडिक्ट (int) '' डिफॉल्टडिक्ट (लैम्ब्डा: 0)' से थोड़ा अधिक स्पष्ट होने के लिए मिलता है। चूंकि लगभग किसी अन्य प्रकार का डिफ़ॉल्ट निर्देश केवल प्रकार के नाम का उपयोग करेगा। –

+0

आह, गलत मूल्य उपज, क्षमा करें। –

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

संग्रह .defaultdict (int) –

+0

@Ryan भी काम करेगा: सच है लेकिन 'lambda: 0' 'int' से अधिक स्पष्ट है ... AFAICT, जब तक डिफॉल्टडिक्ट नहीं आया [2.5], व्यक्तियों की संख्या जो जानता था कि int() उत्पादित 0 [2 से।2] अपवाद के बजाय

-2

नेस्टेड सूची समझ का प्रयोग करें!

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

मुझे नहीं पता कि इस तरह से सूची समझ का उपयोग कैसे किया जाता है। मेरे लिए, यह सिर्फ समाधान को समझने में कठोर बनाता है (कोई इरादा नहीं है)। पठनीयता महत्वपूर्ण है और यह समाधान सिर्फ पठनीय आईएमओ नहीं है। –

+0

रैक्स ने अन्यथा मामूली समस्या के "स्पष्ट" समाधानों के विरोध में "नौकरी पाने के लिए एक साफ पाइथोनियन तरीका" के लिए कहा। –

+0

(1) 'k.keys()' (2) में 'k' के बजाय 'k में 'k' का उपयोग करें, जो भी .values ​​()' (3) के बजाय 'whatever.itervalues ​​()' का उपयोग करें। (Yadda yadda) भाग 'ए' अक्षमता से पहले से अधिक ओवरकिल का निर्माण कर रहा है (4) यह न तो साफ है और न ही पायथन (आईसी | ian) ... लेकिन यह निश्चित रूप से स्पष्ट नहीं है! (5) उत्तरदाताओं की संख्या की गणना करें जिनके तथाकथित छोटी समस्या पर पहला प्रयास एक सामान था। –

0

यहां एक और भिन्नता है।

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

मैं इसके लिए आंशिक हूं क्योंकि उलटा शब्दकोश इतना आम डिजाइन पैटर्न है।

+0

आप अनुरोध के अनुसार ['कुत्ता', 'सांप'] प्राप्त करने के लिए अंतिम पंक्ति में [v [0] के लिए, v ... 0] चाहते हैं। –

+0

(1) .items() के बजाय, .iteritems() का उपयोग करें। (2) आखिरी पंक्ति कुंजी को अनावश्यक रूप से निकालती है; v [v [0] v के लिए inverse.itervalues ​​() में होना चाहिए यदि len (v) == 1' (3) किसी भी मामले में पूरे उलटा हुआ टोकरा ओवरकिल है। –

5

यहाँ एक समाधान है कि केवल dict एक बार traversing की आवश्यकता है:

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) 
+0

यदि कोई मान 3 बार होता है, तो आप 'परिणाम' से मौजूद एक गैर-मौजूद तत्व को निकालने का प्रयास करेंगे ... दस्तावेज़ कहते हैं "" हटाएं (elem) सेट से तत्व elem निकालें। अगर ग्यारह में निहित नहीं है तो KeyError बढ़ाता है सेट। "" –

+0

ठीक है आप! मैंने इसके बजाय त्याग() का उपयोग करने के लिए इसे सही किया है। –

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 ] 

(मुझे आशा है कि यह काम करता है, चूंकि मैं इसे यहां परीक्षण नहीं कर सकता)

+1

काम नहीं करता है अगर शब्दकोश कुंजी में से कोई भी नहीं है। उदाहरण के लिए यदि कोई {none: 1} आउटपुट होना चाहिए [कोई नहीं] लेकिन उपर्युक्त कोड [] का उत्पादन करेगा। इसके अलावा: 'x कोई नहीं है '' x! = None' के लिए बेहतर है। –

+0

टिप्पणी के लिए धन्यवाद! तुम पूर्ण रूप से सही हो। प्रैक्सिस में, शायद ही कभी ऐसा होता है कि कोई भी उपयोग नहीं किया जाता है ... लेकिन फिर भी, कोई भी डमी ऑब्जेक्ट बना सकता है: "डमी = ऑब्जेक्ट()" किसी का उपयोग करने के बजाय। – Juergen

2

सबक्लासिंग के बारे में क्या?

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

इसका एक छोटा मेमोरी पदचिह्न का लाभ है, लेकिन जब भी आप कोई आइटम सेट करते हैं तो आप ओ (एन) खोज करते हैं, इसलिए यह शब्दकोश सारणीकरण विधि से बहुत धीमी है। साथ ही, मुझे लगता है कि आप एक निर्देश के बजाय _inverse के लिए एक सेट का उपयोग कर सकते हैं। –

+0

एक और समस्या: ओपी ने इस बात की कोई बाधा नहीं डाली कि कैसे dict की सामग्री प्राप्त की गई थी। तो कोई उम्मीद करेगा कि 'डेल ए [' बल्ले ']; प्रिंट a.unique_values ​​() 'आउटपुट में' आर्डवर्र्क 'दिखाई देगा, लेकिन दुख की बात यह है कि यह और फिक्सिंग नहीं करता है जिसके लिए और भी अधिक संकल्प और __double__underscores__ की आवश्यकता होगी :-( –