私は非常に単純な関数を作成して、入れ子になっている可能性のある(最も極端な場合は10レベルの深さ)Python辞書)を再帰的に検索し、指定されたキーから見つかった最初の値を返します。
ネストされた辞書でコードが機能しない理由を理解できません。
_def _finditem(obj, key):
if key in obj: return obj[key]
for k, v in obj.items():
if isinstance(v,dict):
_finditem(v, key)
print _finditem({"B":{"A":2}},"A")
_
None
を返します。
ただし、_finditem({"B":1,"A":2},"A")
に対しては機能し、_2
_を返します。
これは単純な間違いだと思いますが、見つけることができません。標準ライブラリまたはcollections
にはすでにこのようなものがあるかもしれませんが、それも見つかりません。
再帰する場合、_finditem
の結果をreturn
する必要があります
def _finditem(obj, key):
if key in obj: return obj[key]
for k, v in obj.items():
if isinstance(v,dict):
return _finditem(v, key) #added return statement
実際のアルゴリズムを修正するには、何も見つからなかった場合に_finditem
がNone
を返すことを認識する必要があります。そのため、早期に戻るのを防ぐために明示的にチェックする必要があります。
def _finditem(obj, key):
if key in obj: return obj[key]
for k, v in obj.items():
if isinstance(v,dict):
item = _finditem(v, key)
if item is not None:
return item
もちろん、辞書にNone
値がある場合は失敗します。その場合、この関数にセンチネルobject()
を設定し、何も見つからない場合にそれを返すことができます。そして、sentinel
をチェックして、何かを見つけたかどうか。
以下は、ネストされた辞書とリストの両方を含む辞書を検索する関数です。結果の値のリストを作成します。
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
「スタック」と 「反復子のスタック」パターン (ガレスリースのクレジット)を使用してこれを行う方法を次に示します。
_def search(d, key, default=None):
"""Return a value corresponding to the specified key in the (possibly
nested) dictionary d. If there is no item with that key, return
default.
"""
stack = [iter(d.items())]
while stack:
for k, v in stack[-1]:
if isinstance(v, dict):
stack.append(iter(v.items()))
break
Elif k == key:
return v
else:
stack.pop()
return default
_
print(search({"B": {"A": 2}}, "A"))
は_2
_を出力します。
評判がないため、@ mgilstonによって提案された受け入れられたソリューションにコメントを追加できませんでした。検索対象のキーがリスト内にある場合、ソリューションは機能しません。
リストの要素をループして再帰関数を呼び出すと、機能を拡張してネストされたリスト内の要素を見つける必要があります。
def _finditem(obj, key):
if key in obj: return obj[key]
for k, v in obj.items():
if isinstance(v,dict):
item = _finditem(v, key)
if item is not None:
return item
Elif isinstance(v,list):
for list_item in v:
item = _finditem(list_item, key)
if item is not None:
return item
print(_finditem({"C": {"B": [{"A":2}]}}, "A"))
複数のネストされた辞書とリストを含む辞書で、一意に指定されたキー(目的の値へのパスを指定する最小限の辞書)を見つける一般的なケースバージョンを作成する必要がありました。
以下の例では、検索するターゲット辞書が作成され、キーはワイルドカード「???」で作成されます。実行すると、値「D」を返します
def lfind(query_list:List, target_list:List, targ_str:str = "???"):
for tval in target_list:
#print("lfind: tval = {}, query_list[0] = {}".format(tval, query_list[0]))
if isinstance(tval, dict):
val = dfind(query_list[0], tval, targ_str)
if val:
return val
Elif tval == query_list[0]:
return tval
def dfind(query_dict:Dict, target_dict:Dict, targ_str:str = "???"):
for key, qval in query_dict.items():
tval = target_dict[key]
#print("dfind: key = {}, qval = {}, tval = {}".format(key, qval, tval))
if isinstance(qval, dict):
val = dfind(qval, tval, targ_str)
if val:
return val
Elif isinstance(qval, list):
return lfind(qval, tval, targ_str)
else:
if qval == targ_str:
return tval
if qval != tval:
break
def find(target_dict:Dict, query_dict:Dict):
result = dfind(query_dict, target_dict)
return result
target_dict = {"A":[
{"key1":"A", "key2":{"key3": "B"}},
{"key1":"C", "key2":{"key3": "D"}}]
}
query_dict = {"A":[{"key1":"C", "key2":{"key3": "???"}}]}
result = find(target_dict, query_dict)
print("result = {}".format(result))