2012-03-21 3 views
37

私はこのような辞書を持ってして下さい。すべてのネストされたPythonの辞書におけるキーの発生やリスト

すべての「id」キーの値を抽出するためにこれをトラバースする最良の方法は何ですか?私は "// id"のようなXPathクエリに相当するものを得たいと思います。 "id"の値は常に文字列です。

だから私の例から、私は必要な出力は基本的には次のとおりです。

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

順序は重要ではありません。

+0

これは 'JSON'と関連がありますか? – hochl

+1

@hochlこれは、mongodbデータベースからのもので、BSONからPythonの 'dict'と' pymongoの 'lists'に解析されています。 –

+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

私が変更する唯一のことは、 'kのdのための' '' kの ''の値、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

と呼ばれる「hexereiソフトウェア」からの回答を検討するために自分自身にそれを借りています。あなたのケースはこれらを持っていないようです。 – Alfe

+1

これは素晴らしいことですが、文字列を直接含むリスト(私の例では忘れてしまったもの)に遭遇すると、同様に問題に遭遇します。私は最後の2行がこれを解決する前に 'dict'のisinstanceチェックを追加すると思います。 –

+0

@Alfeおめでとうございます!私の答えを参照してください。私のスピードはすべての関数をテストしました。あなたの関数は優れています。私の与えた関数に適合しただけです。与えられたオブジェクトに '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

ここで私はそれをやった方法です。

この関数は、ネストされた辞書とリストを含む辞書を再帰的に検索します。 fields_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

この問題は、同じ問題に対していくつかの異なる解決方法を提供するので、このQ/Aは非常に興味深いものです。私はこれらすべての機能を使い、複雑な辞書オブジェクトでそれらをテストしました。テストでは2つの機能を取らなければなりませんでした。なぜなら、結果は多くの場合失敗し、リストやディクテーションを値として返すことをサポートしていなかったからです。

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

すべての機能は、(「ログ」)と同じ辞書オブジェクトを検索するために、同じ針を持っていた:

は、だから私は timeitモジュールを介して100.000反復で他の機能を汲み上げ、出力は次のような結果になりました、このように構成されている:すべての機能は同じ結果を届けますが、時間差は劇的である

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)は、実際には、指定されたオブジェクトがiteritems機能を持っている場合、私は再帰の間に渡された場合の文字列には、チェックしていますことを、主な違いと、アルフェから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は非常に遅く、2番目に遅いのは遠いです。get_recursivleydict_extractを除き、残りはお互いに近いです。 funkeyHoleは、文字列を探している場合にのみ機能します。

ここで面白い学習の側面:)

+0

これはOPの狭いケースを除いて誰にとっても正しい答えです。これは、[jsonpickle](http://jsonpickle.readthedocs.io/en/latest/)がそれを投げることができるすべての厄介なことに取り組んできました。 –

+0

複数のキーを検索する場合は、(1) 'gen_dict_extract(keys、var)'に変更する(2) 'for key in keys:'を2行目としてインデントします。(3)最初の収穫は 'yield {key:v}' –

+0

あなたはリンゴとオレンジを比較しています。ジェネレータを返す関数を実行すると、終了した結果を返す関数を実行するより時間がかかりません。 – kaleissin

関連する問題