2012-03-21 12 views
37

मैं इस तरह एक शब्दकोश है खोजें:अजगर नेस्ट शब्दकोशों में एक महत्वपूर्ण की सभी घटनाओं और सूचियों

{ "id" : "abcde", 
    "key1" : "blah", 
    "key2" : "blah blah", 
    "nestedlist" : [ 
    { "id" : "qwerty", 
     "nestednestedlist" : [ 
     { "id" : "xyz", 
      "keyA" : "blah blah blah" }, 
     { "id" : "fghi", 
      "keyZ" : "blah blah blah" }], 
     "anothernestednestedlist" : [ 
     { "id" : "asdf", 
      "keyQ" : "blah blah" }, 
     { "id" : "yuiop", 
      "keyW" : "blah" }] } ] } 

मूल रूप से एक नेस्टेड सूचियों, शब्दकोशों और तार, मनमाने ढंग से गहराई के साथ शब्दकोश।

प्रत्येक "आईडी" कुंजी के मूल्यों को निकालने के लिए इसे पार करने का सबसे अच्छा तरीका क्या है? मैं "// id" जैसे XPath क्वेरी के बराबर प्राप्त करना चाहता हूं। "आईडी" का मान हमेशा एक स्ट्रिंग है।

तो मेरी उदाहरण से, उत्पादन की आवश्यकता मूल रूप से है:

["abcde", "qwerty", "xyz", "fghi", "asdf", "yuiop"] 

आदेश महत्वपूर्ण नहीं है।

+0

क्या यह 'JSON' के साथ है? – hochl

+1

@ होचल की तरह, यह एक मोंगोड डेटाबेस से है, जो बीएसओएन से एक पायथन 'dict' और' सूचियों 'में pymongo द्वारा पार्स किया गया है। –

+0

** यह भी देखें: ** https://stackoverflow.com/questions/7681301/search-for-a-key-in-a-nested-python-dictionary https://stackoverflow.com/a/16508328/42223 – dreftymac

उत्तर

31
d = { "id" : "abcde", 
    "key1" : "blah", 
    "key2" : "blah blah", 
    "nestedlist" : [ 
    { "id" : "qwerty", 
     "nestednestedlist" : [ 
     { "id" : "xyz", "keyA" : "blah blah blah" }, 
     { "id" : "fghi", "keyZ" : "blah blah blah" }], 
     "anothernestednestedlist" : [ 
     { "id" : "asdf", "keyQ" : "blah blah" }, 
     { "id" : "yuiop", "keyW" : "blah" }] } ] } 


def fun(d): 
    if 'id' in d: 
     yield d['id'] 
    for k in d: 
     if isinstance(d[k], list): 
      for i in d[k]: 
       for j in fun(i): 
        yield j 

>>> list(fun(d)) 
['abcde', 'qwerty', 'xyz', 'fghi', 'asdf', 'yuiop'] 
+1

यह एक पुनरावर्ती जनरेटर है। बहुत अच्छा समाधान! – ovgolovin

+0

एकमात्र चीज जो मैं बदलूंगा वह 'डी के लिए डी' से 'के लिए है, d.items() में मान' d [k] 'के बजाय' value' के बाद के उपयोग के साथ है। – ovgolovin

+0

धन्यवाद, यह बहुत अच्छा काम करता है। बहुत मामूली संशोधन की आवश्यकता है क्योंकि मेरी सूचियों में स्ट्रिंग्स और डिक्ट्स (जिसमें मैंने उल्लेख नहीं किया) शामिल हो सकता है, लेकिन अन्यथा सही है। –

8
def find(key, value): 
    for k, v in value.iteritems(): 
    if k == key: 
     yield v 
    elif isinstance(v, dict): 
     for result in find(key, v): 
     yield result 
    elif isinstance(v, list): 
     for d in v: 
     for result in find(key, d): 
      yield result 
+0

कहा जाता है यदि आप चाहें तो आप dict-elif-Branch को पट्टी कर सकते हैं। आपके मामले में ये प्रतीत नहीं होता है। – Alfe

+1

यह भी बहुत अच्छा काम करता है, लेकिन इसी तरह मुद्दों में भी चलता है यदि यह ऐसी सूची से मुकाबला करता है जिसमें सीधे स्ट्रिंग होती है (जिसे मैं अपने उदाहरण में शामिल करना भूल गया)। मुझे लगता है कि पिछले दो लाइनों से पहले इसे ''dict'' के लिए 'आईइंस्टेंस' चेक में जोड़ना है। –

+0

@ एल्फ बधाई हो! मेरा जवाब देखें, जहां मैंने सभी कार्यों का परीक्षण किया है, आपका फ़ंक्शन बकाया है, केवल आपके फ़ंक्शन पर मेरे अनुकूलन से पीछे हट गया है, जहां मैंने जांच की है कि दिए गए ऑब्जेक्ट में 'iteritems()' फ़ंक्शन –

3
d = { "id" : "abcde", 
    "key1" : "blah", 
    "key2" : "blah blah", 
    "nestedlist" : [ 
    { "id" : "qwerty", 
     "nestednestedlist" : [ 
     { "id" : "xyz", "keyA" : "blah blah blah" }, 
     { "id" : "fghi", "keyZ" : "blah blah blah" }], 
     "anothernestednestedlist" : [ 
     { "id" : "asdf", "keyQ" : "blah blah" }, 
     { "id" : "yuiop", "keyW" : "blah" }] } ] } 


def findkeys(node, kv): 
    if isinstance(node, list): 
     for i in node: 
      for x in findkeys(i, kv): 
       yield x 
    elif isinstance(node, dict): 
     if kv in node: 
      yield node[kv] 
     for j in node.values(): 
      for x in findkeys(j, kv): 
       yield x 

print list(findkeys(d, 'id')) 
2

यहाँ कैसे मैंने किया है।

यह फ़ंक्शन रिक्त रूप से नेस्टेड शब्दकोश और सूचियों वाले शब्दकोश को खोजता है। यह field_found नामक एक सूची बनाता है, जिसमें फ़ील्ड मिलने पर हर बार मान होता है। 'फ़ील्ड' वह कुंजी है जिसे मैं शब्दकोश में ढूंढ रहा हूं और इसकी नेस्टेड सूचियां और शब्दकोश।

 
def get_recursively(search_dict, field): 
    """Takes a dict with nested lists and dicts, 
    and searches all dicts for a key of the field 
    provided. 
    """ 
    fields_found = [] 

    for key, value in search_dict.iteritems(): 

     if key == field: 
      fields_found.append(value) 

     elif isinstance(value, dict): 
      results = get_recursively(value, field) 
      for result in results: 
       fields_found.append(result) 

     elif isinstance(value, list): 
      for item in value: 
       if isinstance(item, dict): 
        more_results = get_recursively(item, field) 
        for another_result in more_results: 
         fields_found.append(another_result) 

    return fields_found 
1

एक और भिन्नता है, जो परिणाम मिले करने के लिए नेस्टेड पथ शामिल (ध्यान दें: इस संस्करण सूचियों पर विचार नहीं करता):

def find_all_items(obj, key, keys=None): 
    """ 
    Example of use: 
    d = {'a': 1, 'b': 2, 'c': {'a': 3, 'd': 4, 'e': {'a': 9, 'b': 3}, 'j': {'c': 4}}} 
    for k, v in find_all_items(d, 'a'): 
     print "* {} = {} *".format('->'.join(k), v)  
    """ 
    ret = [] 
    if not keys: 
     keys = [] 
    if key in obj: 
     out_keys = keys + [key] 
     ret.append((out_keys, obj[key])) 
    for k, v in obj.items(): 
     if isinstance(v, dict): 
      found_items = find_all_items(v, key, keys=(keys+[k])) 
      ret += found_items 
    return ret 
0

यहाँ यह पर मेरे चाकू है:

def keyHole(k2b,o): 
    # print "Checking for %s in "%k2b,o 
    if isinstance(o, dict): 
    for k, v in o.iteritems(): 
     if k == k2b and not hasattr(v, '__iter__'): yield v 
     else: 
     for r in keyHole(k2b,v): yield r 
    elif hasattr(o, '__iter__'): 
    for r in [ keyHole(k2b,i) for i in o ]: 
     for r2 in r: yield r2 
    return 

Ex.:

>>> findMe = {'Me':{'a':2,'Me':'bop'},'z':{'Me':4}} 
>>> keyHole('Me',findMe) 
<generator object keyHole at 0x105eccb90> 
>>> [ x for x in keyHole('Me',findMe) ] 
['bop', 4] 
28

मुझे यह क्यू/ए बहुत दिलचस्प लगता है, क्योंकि यह एक ही समस्या के लिए कई अलग-अलग समाधान प्रदान करता है। मैंने इन सभी कार्यों को लिया और उन्हें एक जटिल शब्दकोश वस्तु के साथ परीक्षण किया। मुझे परीक्षण से दो कार्य करना पड़ता था, क्योंकि उन्हें कई असफल परिणाम होते थे और वे मूल्यों के रूप में लौटने वाली सूचियों या डिक्ट्स का समर्थन नहीं करते थे, जो मुझे आवश्यक लगता है, क्योंकि लगभग किसी भी डेटा के लिए एक फ़ंक्शन तैयार किया जाना चाहिए।

तो मैं timeit मॉड्यूल के माध्यम से 100,000 पुनरावृत्तियों में अन्य कार्यों पंप और आउटपुट निम्न परिणाम के लिए आया था:

0.11 usec/pass on gen_dict_extract(k,o) 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
6.03 usec/pass on find_all_items(k,o) 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
0.15 usec/pass on findkeys(k,o) 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
1.79 usec/pass on get_recursively(k,o) 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
0.14 usec/pass on find(k,o) 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
0.36 usec/pass on dict_extract(k,o) 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

सभी कार्यों ('प्रवेश') और एक ही शब्दकोश वस्तु के लिए खोज करने के लिए एक ही सुई था , जिसे इस तरह बनाया गया है:

o = { 'temparature': '50', 
     'logging': { 
     'handlers': { 
      'console': { 
      'formatter': 'simple', 
      'class': 'logging.StreamHandler', 
      'stream': 'ext://sys.stdout', 
      'level': 'DEBUG' 
      } 
     }, 
     'loggers': { 
      'simpleExample': { 
      'handlers': ['console'], 
      'propagate': 'no', 
      'level': 'INFO' 
      }, 
     'root': { 
      'handlers': ['console'], 
      'level': 'DEBUG' 
     } 
     }, 
     'version': '1', 
     'formatters': { 
     'simple': { 
      'datefmt': "'%Y-%m-%d %H:%M:%S'", 
      'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s' 
     } 
     } 
    }, 
    'treatment': {'second': 5, 'last': 4, 'first': 4}, 
    'treatment_plan': [[4, 5, 4], [4, 5, 4], [5, 5, 5]] 
} 

सभी कार्यों ने एक ही परिणाम दिया, लेकिन समय अंतर नाटकीय हैं!फ़ंक्शन gen_dict_extract(k,o) मेरा फ़ंक्शन यहां फ़ंक्शंस से अनुकूलित किया गया है, वास्तव में यह मुख्य अंतर के साथ find फ़ंक्शन एल्फ़ से बहुत अधिक है, कि मैं जांच कर रहा हूं कि दिए गए ऑब्जेक्ट में इटिरिटैम फ़ंक्शन है, यदि स्ट्रिंग के दौरान स्ट्रिंग पास हो जाती है:

def gen_dict_extract(key, var): 
    if hasattr(var,'iteritems'): 
     for k, v in var.iteritems(): 
      if k == key: 
       yield v 
      if isinstance(v, dict): 
       for result in gen_dict_extract(key, v): 
        yield result 
      elif isinstance(v, list): 
       for d in v: 
        for result in gen_dict_extract(key, d): 
         yield result 

तो यह संस्करण यहां कार्यों का सबसे तेज़ और सुरक्षित है। और find_all_items अविश्वसनीय रूप से धीमा है और दूसरे धीमे get_recursivley से दूर है जबकि बाकी, dict_extract को छोड़कर, एक-दूसरे के करीब है। फ़ंक्शन fun और keyHole केवल तभी काम करते हैं जब आप स्ट्रिंग की तलाश में हैं।

दिलचस्प सीखने का पहलू :)

+0

ओपी के संकीर्ण मामले के अलावा यह किसी के लिए सही जवाब है। इसने हर ग़लत चीज़ पर काम किया है [jsonpickle] (http://jsonpickle.readthedocs.io/en/latest/) इसे फेंक सकता है। –

+0

यदि आप कई कुंजियों की खोज करना चाहते हैं, तो बस: (1) 'gen_dict_extract (कुंजी, var)' (2) में बदलें 'कुंजी में कुंजी के लिए' रखें: 'लाइन 2 के रूप में और बाकी को इंडेंट करें (3) बदलें उपज {कुंजी: v} ' –

+0

पर पहली उपज आप संतरे से सेब की तुलना कर रहे हैं। एक जेनरेटर लौटने वाले फ़ंक्शन को चलाने से एक फ़ंक्शन चलाने से कम समय लगता है जो एक पूर्ण परिणाम देता है। सभी जेनरेटर समाधानों के लिए 'अगला (फ़ंक्शननाम (के, ओ) 'पर समय-समय पर प्रयास करें। – kaleissin

संबंधित मुद्दे