概要
*.aa.*.bb.*
のようなパスを書いて複雑な dict から値のリストを取り出す方法を紹介します。
準備
下記を定義します。
def extract_all(data, keys_str, values=None):
if values is None:
values = []
if keys_str == '':
values.append(data)
else:
keys_list = keys_str.split('.')
key = keys_list.pop(0)
if key == '*':
if type(data) == dict:
data = data.values()
for v in data:
extract_all(v, '.'.join(keys_list), values)
elif key in data:
v = data[key]
extract_all(v, '.'.join(keys_list), values)
return values
これだけです。
使い方
# 複雑な dict
data = dict(
a=dict(aa=[
dict(bb=[111, 112]),
dict(bb=[121, 122]),
]),
b=dict(aa=[
dict(bb=[211, 212]),
dict(bb=[221, 222]),
]),
c=dict(),
)
print(extract_all(data, '*'))
# 出力:
# [{'aa': [{'bb': [111, 112]}, {'bb': [121, 122]}]},
# {'aa': [{'bb': [211, 212]}, {'bb': [221, 222]}]},
# {}]
print(extract_all(data, '*.aa'))
# 出力:
# [[{'bb': [111, 112]}, {'bb': [121, 122]}],
# [{'bb': [211, 212]}, {'bb': [221, 222]}]]
print(extract_all(data, '*.aa.*'))
# 出力:
# [{'bb': [111, 112]},
# {'bb': [121, 122]},
# {'bb': [211, 212]},
# {'bb': [221, 222]}]
print(extract_all(data, '*.aa.*.bb'))
# 出力:
# [[111, 112], [121, 122], [211, 212], [221, 222]]
print(extract_all(data, '*.aa.*.bb.*'))
# 出力:
# [111, 112, 121, 122, 211, 212, 221, 222]
- 該当のパスが存在しない場合エラーにはらず、単純にリストから除外されます。
まとめ
不安定かつ複雑なデータをあつかう際に上記があれば、個々にエラーハンドリング書く必要なくなるのでソースの可読性をあげられます。
また、 Python で複雑な構造の dict オブジェクトをセレクタ的にアクセスできるようにする という記事で、パスの途中が無い場合にフルパスでエラー出してくれるクラスを紹介してますので、よろしければそちらもご覧ください。