このような辞書があります:
{ "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」キーの値を抽出する最良の方法は何ですか? 「// id」のようなXPathクエリに相当するものを実現したい。 「id」の値は常に文字列です。
したがって、私の例から、私が必要とする出力は基本的に次のとおりです。
["abcde", "qwerty", "xyz", "fghi", "asdf", "yuiop"]
順序は重要ではありません。
このQ/Aは、同じ問題に対して複数の異なるソリューションを提供するため、非常に興味深いことがわかりました。これらすべての機能を使用して、複雑な辞書オブジェクトでテストしました。多くの失敗結果が必要であり、リストまたは辞書を値として返すことをサポートしていなかったため、テストから2つの関数を取り出す必要がありました。関数はほとんどany =来るデータ。
だから私は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)
は、ここの関数から適応した私の関数です。実際には、Alfeのfind
関数とほとんど同じですが、主な違いは、特定のオブジェクトにiteritemsがあるかどうかを確認することです関数、再帰中に文字列が渡される場合:
_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_recursivley
_から大きく外れていますが、_dict_extract
_を除く残りの部分は互いに近接しています。関数fun
とkeyHole
は、文字列を探している場合にのみ機能します。
ここで興味深い学習の側面:)
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']
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
編集:@Anthonは、これが直接ネストされたリストでは機能しないことに気付きました。入力にこれがある場合、これを使用できます。
def find(key, value):
for k, v in (value.iteritems() if isinstance(value, dict) else
enumerate(value) if isinstance(value, list) else []):
if k == key:
yield v
Elif isinstance(v, (dict, list)):
for result in find(key, v):
yield result
しかし、元のバージョンは理解しやすいと思うので、そのままにします。
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'))
yield from
を使用してトップレベルのリストを受け入れて、@ hexerei-softwareの優れた答えを繰り返したいと思いました。
def gen_dict_extract(var, key):
if isinstance(var, dict):
for k, v in var.items():
if k == key:
yield v
if isinstance(v, (dict, list)):
yield from gen_dict_extract(v, key)
Elif isinstance(var, list):
for d in var:
yield from gen_dict_extract(d, key)
見つかった結果へのネストされたパスを含む別のバリエーション(注:このバージョンはリストを考慮しません):
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
ここに私がそれをした方法があります。
この関数は、ネストされた辞書とリストを含む辞書を再帰的に検索します。 fields_foundというリストを作成します。このリストには、フィールドが見つかるたびに値が含まれます。 「フィールド」は、辞書およびそのネストされたリストと辞書で探しているキーです。
def get_recursively(search_dict、field): "" "ネストされたリストと辞書を使用して辞書を取得し、 、フィールドのキーをすべての辞書で検索します "" " fields_found = [] for search_dict.iteritems(): if key == field: fields_found.append(value) Elif isinstance(value、dict): results = get_recursively(value、field) 結果の結果: fields_found.append(result) Elif isinstance(value、list): value in item: if isinstance(item、dict): more_results = get_recursively(item、field) more_results: fields_found.append(another_result) return fields_found
ここに私の刺し傷があります:
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
>>> 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]