静かなる名辞

pythonとプログラミングのこと


【python】ネストされた辞書をflattenしてみる(一つの辞書にまとめる)

 こんな辞書を考える。

d = {1:"a",
     2:{"b":"hoge"},
     3:{"c":"fuga",
        "piyo":["foo", "bar"], 
        "buzz":{"d":"hogehoge"}}}

 次のような結果を得るにはどうすれば良いか。

{(1,): 'a',
 (2, 'b'): 'hoge',
 (3, 'c'): 'fuga',
 (3, 'piyo'): ['foo', 'bar'],
 (3, 'buzz', 'd'): 'hogehoge'}

 やってみよう。再帰で。

from pprint import pprint
d = {1:"a",
     2:{"b":"hoge"},
     3:{"c":"fuga",
        "piyo":["foo", "bar"], 
        "buzz":{"d":"hogehoge"}}}

def flatten_dict(d, pre_lst=None, result=None):
    if result is None:
        result = {}
    if pre_lst is None:
        pre_lst = []
    for k,v in d.items():
        if isinstance(v, dict):
            flatten_dict(v, pre_lst=pre_lst+[k], result=result)
        else:
            result[tuple(pre_lst+[k])] = v
    return result

pprint(flatten_dict(d))
""" =>
{(1,): 'a',
 (2, 'b'): 'hoge',
 (3, 'buzz', 'd'): 'hogehoge',
 (3, 'c'): 'fuga',
 (3, 'piyo'): ['foo', 'bar']}
"""

 できた。

 途中のキーをリストにしてたどりながら末端まで再帰でたどり、子が辞書でなくなったときにキーのリストをtupleにしてキーとして結果の辞書に入れる。

 ただ、リストオブジェクトを毎回作り直しているのがダサい。これはスタック風に使えば一つのオブジェクトで行けるはずなので、そうしてみよう。

from pprint import pprint
d = {1:"a",
     2:{"b":"hoge"},
     3:{"c":"fuga",
        "piyo":["foo", "bar"], 
        "buzz":{"d":"hogehoge"}}}

def flatten_dict(d, pre_lst=[], result=None):
    if result is None:
        result = {}
    for k,v in d.items():
        pre_lst.append(k)
        if isinstance(v, dict):
            flatten_dict(v, pre_lst=pre_lst, result=result)
        else:
            result[tuple(pre_lst)] = v
        pre_lst.pop(-1)
    return result

pprint(flatten_dict(d))
""" =>
{(1,): 'a',
 (2, 'b'): 'hoge',
 (3, 'buzz', 'd'): 'hogehoge',
 (3, 'c'): 'fuga',
 (3, 'piyo'): ['foo', 'bar']}
"""

 たぶんちゃんと動いている。だからどうしたって話なのだが。

 頭の体操としては面白かった。実用的な用途は? あまりないと思う。