Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a pythonic way to process tree-structured dict keys?

I'm looking for a pythonic idiom to turn a list of keys and a value into a dict with those keys nested. For example:

dtree(["a", "b", "c"]) = 42
    or
dtree("a/b/c".split(sep='/')) = 42

would return the nested dict:

{"a": {"b": {"c": 42}}}

This could be used to turn a set of values with hierarchical keys into a tree:

dtree({
    "a/b/c": 10,
    "a/b/d": 20,
    "a/e": "foo",
    "a/f": False,
    "g": 30 })

would result in:

{   "a": {
        "b": {
            "c": 10,
            "d": 20 },
        "e": foo",
        "f": False },
    "g": 30 }

I could write some FORTRANish code to do the conversion using brute force and multiple loops and maybe collections.defaultdict, but it seems like a language with splits and joins and slices and comprehensions should have a primitive that turns a list of strings ["a","b","c"] into nested dict keys ["a"]["b"]["c"]. What is the shortest way to do this without using eval on a dict expression string?

like image 568
Dave Avatar asked Jul 20 '16 19:07

Dave


1 Answers

I'm looking for a pythonic idiom to turn a list of keys and a value into a dict with those keys nested.

reduce(lambda v, k: {k: v}, reversed("a/b/c".split("/")), 42)

This could be used to turn a set of values with hierarchical keys into a tree

def hdict(keys, value, sep="/"):
    return reduce(lambda v, k: {k: v}, reversed(keys.split(sep)), value)

def merge_dict(trg, src):
    for k, v in src.items():
        if k in trg:
            merge_dict(trg[k], v)
        else:
            trg[k] = v

def hdict_from_dict(src):
    result = {}
    for sub_hdict in map(lambda kv: hdict(*kv), src.items()):
        merge_dict(result, sub_hdict)
    return result

data = {
    "a/b/c": 10,
    "a/b/d": 20,
    "a/e": "foo",
    "a/f": False,
    "g": 30 }

print(hdict_from_dict(data))

Another overall solution using collections.defaultdict

import collections

def recursive_dict():
    return collections.defaultdict(recursive_dict)

def dtree(inp):
    result = recursive_dict()
    for keys, value in zip(map(lambda s: s.split("/"), inp), inp.values()):
        reduce(lambda d, k: d[k], keys[:-1], result)[keys[-1]] = value
    return result

import json
print(json.dumps(dtree({
    "a/b/c": 10,
    "a/b/d": 20,
    "a/e": "foo",
    "a/f": False,
    "g": 30 }), indent=4))
like image 118
Michael Hoff Avatar answered Oct 31 '22 12:10

Michael Hoff