Lift up all occurrences of a type in a nested dictionary to a top level key - python

I have a need in a project to find all of a given type in a nested dictionary and move them all to a top level key in the same dictionary.
So far I have the below code, which seems to work. In the example I'm looking for all the items that are integers and moving them to a 'numbers' key.
I'd prefer it if the lift_numbers_to_top function made and returned a copy of the dictionary rather than editing it in place, but I haven't been able to work out a nice way to pass the copy and the numbers back from the recursive function to itself, if that makes sense.
a_dictionary = {
"one": 1,
"two": 2,
"text": "Hello",
"more_text": "Hi",
"internal_dictionary": {
"three": 3,
"two": 2,
"even_more_text": "Hey",
"another_internal_dictionary": {
"four": 4,
"five": 5,
"last_text": "howdy"
}
}
}
def extract_integers(dictionary, level_key=None):
numbers = {}
for key in dictionary:
if type(dictionary[key]) == int:
numbers[level_key + "__" + key if level_key else key] = dictionary[key]
return numbers
def lift_numbers_to_top(dictionary, level_key=None):
numbers = {}
if type(dictionary) == dict:
numbers = extract_integers(dictionary, level_key)
for key in numbers:
keyNumber = key.split('__')[-1]
del dictionary[keyNumber]
for key in dictionary:
numbers = {**numbers, **lift_numbers_to_top(dictionary[key], key)}
return numbers
a_dictionary['numbers'] = lift_numbers_to_top(a_dictionary)
print(a_dictionary)
Result:
{
'text': 'Hello',
'more_text': 'Hi',
'internal_dictionary': {
'even_more_text': 'Hey',
'another_internal_dictionary': {
'last_text': 'howdy'
},
},
'numbers': {
'one': 1,
'two': 2,
'internal_dictionary__two': 2,
'internal_dictionary__three': 3,
'another_internal_dictionary__four': 4,
'another_internal_dictionary__five': 5,
}
}

Use a match function to determine what to lift, and pass along the target object where you move key-value pairs to to recursive calls. If that target is missing, you know the current call is for the top-level. The match function should return the new key for the new dictionary.
To produce a new dictionary, just produce a new dictionary and put recursion results into that object.
I prefer to use #singledispatch() to handle different types when recursing:
from functools import singledispatch
#singledispatch
def lift_values(obj, match, targetname=None, **kwargs):
"""Lift key-value pairs from a nested structure to the top
For key-value pairs anywhere in the nested structure, if
match(path, value) returns a value other than `None`, the
key-value pair is moved to the top-level dictionary when targetname
is None, or to a new dictionary stored under targetname is not None,
using the return value of the match function as the key. path
is the tuple of all keys and indices leading to the value.
For example, for an input
{'foo': True, 'bar': [{'spam': False, 'ham': 42}]}
and the match function lambda p, v: p if isinstance(v, bool) else None
and targetname "flags", this function returns
{'flags': {('foo',): True, ('bar', 0, 'spam'): False}, 'bar': [{'ham': 42}]}
"""
# leaf nodes, no match testing needed, no moving of values
return obj
#lift_values.register(list)
def _handle_list(obj, match, _path=(), **kwargs):
# list values, no lifting, just passing on the recursive call
return [lift_values(v, match, _path=_path + (i,), **kwargs)
for i, v in enumerate(obj)]
#lift_values.register(dict)
def _handle_list(obj, match, targetname=None, _path=(), _target=None):
result = {}
if _target is None:
# this is the top-level object, key-value pairs are lifted to
# a new dictionary stored at this level:
if targetname is not None:
_target = result[targetname] = {}
else:
# no target name? Lift key-value pairs into the top-level
# object rather than a separate sub-object.
_target = result
for key, value in obj.items():
new_path = _path + (key,)
new_key = match(new_path, value)
if new_key is not None:
_target[new_key] = value
else:
result[key] = lift_values(
value, match, _path=new_path, _target=_target)
return result
I included a dispatch function for lists; your sample doesn't use lists, but these are common in JSON data structures so I anticipate you probably want it anyway.
The match function must accept two arguments, the path to the object this key-value pair was found in, and the value. It should return a new key to use or None if not to lift the value.
For your case, the match function would be:
def lift_integers(path, value):
if isinstance(value, int):
return '__'.join(path[-2:])
result = lift_values(a_dictionary, lift_integers, 'numbers')
Demo on your sample input dictionary:
>>> from pprint import pprint
>>> def lift_integers(path, value):
... if isinstance(value, int):
... return '__'.join(path[-2:])
...
>>> lift_values(a_dictionary, lift_integers, 'numbers')
{'numbers': {'one': 1, 'two': 2, 'internal_dictionary__three': 3, 'internal_dictionary__two': 2, 'another_internal_dictionary__four': 4, 'another_internal_dictionary__five': 5}, 'text': 'Hello', 'more_text': 'Hi', 'internal_dictionary': {'even_more_text': 'Hey', 'another_internal_dictionary': {'last_text': 'howdy'}}}
>>> pprint(_)
{'internal_dictionary': {'another_internal_dictionary': {'last_text': 'howdy'},
'even_more_text': 'Hey'},
'more_text': 'Hi',
'numbers': {'another_internal_dictionary__five': 5,
'another_internal_dictionary__four': 4,
'internal_dictionary__three': 3,
'internal_dictionary__two': 2,
'one': 1,
'two': 2},
'text': 'Hello'}
Personally, I'd use the full path as the key in the lifted dictionary to avoid name clashes; either by joining the full path into a new string key with some unique delimiter, or just by making the path tuple itself the new key:
>>> lift_values(a_dictionary, lambda p, v: p if isinstance(v, int) else None, 'numbers')
{'numbers': {('one',): 1, ('two',): 2, ('internal_dictionary', 'three'): 3, ('internal_dictionary', 'two'): 2, ('internal_dictionary', 'another_internal_dictionary', 'four'): 4, ('internal_dictionary', 'another_internal_dictionary', 'five'): 5}, 'text': 'Hello', 'more_text': 'Hi', 'internal_dictionary': {'even_more_text': 'Hey', 'another_internal_dictionary': {'last_text': 'howdy'}}}
>>> pprint(_)
{'internal_dictionary': {'another_internal_dictionary': {'last_text': 'howdy'},
'even_more_text': 'Hey'},
'more_text': 'Hi',
'numbers': {('internal_dictionary', 'another_internal_dictionary', 'five'): 5,
('internal_dictionary', 'another_internal_dictionary', 'four'): 4,
('internal_dictionary', 'three'): 3,
('internal_dictionary', 'two'): 2,
('one',): 1,
('two',): 2},
'text': 'Hello'}

You can use walk through the dict recursively and pop all elements with values as an int to create a new dict
>>> def extract(d):
... new_d = {}
... for k,v in d.items():
... if type(v) == int:
... new_d[k] = d[k]
... elif type(v) == dict:
... for k2,v2 in extract(v).items():
... new_d[k2 if '__' in k2 else k+'__'+k2] = v2
... return new_d
...
>>> a_dictionary['numbers'] = extract(a_dictionary)
>>> pprint(a_dictionary)
{'internal_dictionary': {'another_internal_dictionary': {'last_text': 'howdy'},
'even_more_text': 'Hey'},
'more_text': 'Hi',
'numbers': {'another_internal_dictionary__five': 5,
'another_internal_dictionary__four': 4,
'internal_dictionary__three': 3,
'internal_dictionary__two': 2,
'one': 1,
'two': 2},
'text': 'Hello'}

Related

How to change all the values in a nested dictionary in python?

I have sample data that looks like this (nested and is dynamic so it does change on the fly):
...
"counts":{
"1":{
"1":21082,
"2":14999
},
"2":{
"1":9180,
"2":10023
}
},
...
I need to recursively go through and replace all the values with -1, so the result will be something like:
...
"counts":{
"1":{
"1":-1,
"2":-1
},
"2":{
"1":-1,
"2":-1
}
},
...
I can do it if the dictionary is only one level deep like so:
result['counts'] = {x: '-1' for x in result['counts']}
How do I do it for 2 or more levels on the fly?
Also sometimes the key's can be like 1(1, 2)` or other things like so:
...
"counts":{
"(1, 2)":{
"1":21082,
"2":14999
},
"(2, 1)":{
"1":9180,
"2":10023
}
},
...
Is there any easy way to do this?
Use a recursive function that calls itself on each nested dict:
>>> def replace_value(d, new_val):
... return {
... k: replace_value(v, new_val) if isinstance(v, dict) else new_val
... for k, v in d.items()
... }
...
>>> replace_value({"foo": {"foo": "bar"}}, -1)
{'foo': {'foo': -1}}
Alternate version with the base case outside the comprehension:
>>> def replace_value(d, new_val):
... if not isinstance(d, dict):
... return new_val
... return {k: replace_value(v, new_val) for k, v in d.items()}
...
>>> replace_value({"foo": {"foo": "bar"}}, -1)
{'foo': {'foo': -1}}
My choice for similar tasks is remap from boltons
>>> def visitor(path, key, value):
... if not isinstance(value, dict):
... return key, -1
... return True
...
>>> from boltons.iterutils import remap # pip install boltons
>>> d
{'counts': {'1': {'1': 21082, '2': 14999}, '2': {'1': 9180, '2': 10023}}}
>>> remap(d, visit=visitor)
{'counts': {'1': {'1': -1, '2': -1}, '2': {'1': -1, '2': -1}}}
The visit callable accepts a path, key, and value, and should return the new key and value (or return True as a shorthand to keep old item unmodified).
I prefer this to a recursive function, because it uses a stack-based iterative approach. It is too easy to blow past the recursion limit in Python when dealing with nested data structures.

How to set dictionary element with key as array?

If I wanted to use an array to get a value from a dictionary, I would do something like this:
def get_dict_with_arr(d, arr):
accumulator = d
for elem in arr:
accumulator = accumulator[elem]
return accumulator
and use it like this:
test_dict = {
'this': {
'is': {
'it': 'test'
}
}
}
get_dict_with_arr(test_dict, ['this', 'is', 'it']) # returns 'test'
My question is, how may I write a function that sets the value instead of getting it? Basically I want to write a set_dict_with_arr(d, arr, value) function.
Try:
def set_dict_with_arr(d, arr, value):
cur_d = d
for v in arr[:-1]:
cur_d.setdefault(v, {})
cur_d = cur_d[v]
cur_d[arr[-1]] = value
return d
test_dict = {"this": {"is": {"it": "test"}}}
test_dict = set_dict_with_arr(test_dict, ["this", "is", "it"], "new value")
print(test_dict)
Prints:
{"this": {"is": {"it": "new value"}}}

How to flatten deep dictionary; surrounding any child dictionary keys with square brackets

Maybe this is called something specifically - I just don't know.
If you have some data in a dictionary like so:
data1 = {
"first": {"a":24,
"b": {"green": {"look": 3,
"out": "Nope"},
"apple": True}},
"third": {"x": {"word": 8}, "y": -1, "z": 26},
"fifth": {"ae": [0, None, 2.0, 3.0],
"e": None}
}
Is there some function from some module that does this (or some other tool)?
data2 = {
"first[a]": 24,
"first[b][green][look]": 3,
"first[b][green][out]": "Nope",
"first[b][apple]": True,
"third[x][word]": 8,
"third[y]": -1,
"third[z]": 26,
"fifth[ae][0]": 0,
"fifth[ae][1]": None,
"fifth[ae][2]": 2.0,
"fifth[ae][3]": 3.0,
"fifth[e]": None
}
I made a function to get the flattened keys/values like:
def get_deep_dict_keys_gen(data, sep="."):
if isinstance(data, list): iter = map(lambda t: (str(t[0]), t[1]), enumerate(data))
elif isinstance(data, dict): iter = data.items()
else: iter = []
for k, v in iter:
if isinstance(v, dict) or isinstance(v, list):
yield from [k + sep + x for x in get_deep_dict_keys_gen(v, sep=sep)]
else:
yield k
# I can make the flatten dictionary again using a function _g(data, path), that gets the deep path value.
{x: _g(data1, x) for x in util.get_deep_dict_keys_gen(data1)}
# Which makes:
{
'fifth.ae.0': 0,
'fifth.ae.1': None,
'fifth.ae.2': 2.0,
'fifth.ae.3': 3.0,
'fifth.e': None,
'first.a': 24,
'first.b.apple': True,
'first.b.green.look': 3,
'first.b.green.out': 'Nope',
'third.x.word': 8,
'third.y': -1,
'third.z': 26
}
I just don't know how to recursively get the keys w/ the data values. Maybe there is some tool that does this; I see it done in the browser with website interactions. Taking some json on a page - and making a post request in the layout of data2.
Edit: Thanks for the help, Arun Augustine.
Try this,
from itertools import chain, starmap
def flatten_dict(dictionary):
"""Flatten a nested dictionary structure"""
def unpack(parent_key, parent_value):
"""Unpack one level of nesting in a dictionary"""
try:
items = parent_value.items()
except AttributeError:
# parent_value was not a dict, no need to flatten
yield (parent_key, parent_value)
else:
for key, value in items:
if type(value) == list:
for k, v in enumerate(value):
yield (parent_key + '[' + key + ']' + '['+str(k)+']', v)
else:
yield (parent_key + '['+key+']', value)
while True:
# Keep unpacking the dictionary until all value's are not dictionary's
dictionary = dict(chain.from_iterable(starmap(unpack, dictionary.items())))
if not any(isinstance(value, dict) for value in dictionary.values()):
break
return dictionary
# Input Dict
input_dict = {
"first": {"a": 24,
"b": {"green": {"look": 3,
"out": "Nope"},
"apple": True}},
"third": {"x": {"word": 8}, "y": -1, "z": 26},
"fifth": {"ae": [0, None, 2.0, 3.0],
"e": None}
}
print(flatten_dict(input_dict))
OutPut:
{
'first[a]': 24,
'first[b][green][look]': 3,
'first[b][green][out]': 'Nope',
'first[b][apple]': True,
'third[x][word]': 8,
'third[y]': -1,
'third[z]': 26,
'fifth[ae][0]': 0,
'fifth[ae][1]': None,
'fifth[ae][2]': 2.0,
'fifth[ae][3]': 3.0,
'fifth[e]': None
}
My assumption in this below data is list element does not contains either dict or list.
data1 = {
"first": {"a":24,
"b": {"green": {"look": 3,
"out": "Nope"},
"apple": True}},
"third": {"x": {"word": 8}, "y":-1, "z": 26},
"fifth": {"ae": [0, None, 2.0, 3.0],
"e": None}
}
add_dict method is generator and responsible for looping over each key and its value. Given value is dictionary object, the method will call itself.
def add_dict(_dict):
for key, value in _dict.items():
if isinstance(value, dict):
for indexof, value in add_dict(value):
yield '[{0}]'.format(key) + indexof, value
elif isinstance(value, list):
for indexof, value in add_list(value):
yield '[{0}]'.format(key) + indexof, value
else:
yield '[{0}]'.format(key), value
add_list method is generator. It's element does not contains either dict or list and so it looks as simple.
def add_list(_list):
for index, elem in enumerate(_list):
yield '[{0}]'.format(index), elem
flatten is a main method and generator. It's element contains dict only and so it looks as simple.
def flatten(data):
for key, value in data.items():
for indexof, value in add_dict(value):
yield '{0}'.format(key) + indexof, value
print(dict(flatten(data1)))
Output of above execution
{'fifth[ae][0]': 0,
'fifth[ae][1]': None,
'fifth[ae][2]': 2.0,
'fifth[ae][3]': 3.0,
'fifth[e]': None,
'first[a]': 24,
'first[b][apple]': True,
'first[b][green][look]': 3,
'first[b][green][out]': 'Nope',
'third[x][word]': 8,
'third[y]': -1,
'third[z]': 26}

Better way to filter the rows with python

import pprint
full_key_list = set(["F1", "F2", "F3", "F4", "F5"]) # all expected field
filt_key_list = set(["F2", "F5"]) # fields should not be included
cont_list = [] # stores all filtered documents
read_in_cont1 = { "F1" : 1, "F2" : True, "F3" : 'abc', "F4" : 130, "F5" : 'X1Z'} # document1
read_in_cont2 = { "F1" : 2, "F2" : False, "F3" : 'efg', "F4" : 100, "F5" : 'X4Z'} # document1
read_in_cont3 = { "F1" : 3, "F2" : True, "F3" : 'acd', "F4" : 400, "F5" : 'X2Z'} # document1
# assume that read_in_conts contains list of documents
read_in_conts = [read_in_cont1, read_in_cont2, read_in_cont3]
for one_item in read_in_conts: # for each document in the list
cont_dict = {}
for key, value in one_item.iteritems():
if key not in filt_key_list: # if the field should be included
cont_dict[key] = value # add this field to the temporary document
cont_list.append(cont_dict)
pprint.pprint(cont_list)
Output:
[{'F1': 1, 'F3': 'abc', 'F4': 130},
{'F1': 2, 'F3': 'efg', 'F4': 100},
{'F1': 3, 'F3': 'acd', 'F4': 400}]
Here is what I want to achieve:
Given an original raw collection of documents (i.e. read_in_conts for simulation),
I need to filter the fields so that they are not included in further process. Above
is my implementation in Python. However, I think it is too heavy and expect to see
a clean solution for this task.
Thank you
cont_list = [dict((k,v) for k,v in d.iteritems() if k not in filt_key_list)
for d in read_in_conts]
or if you want a slightly more factored version:
filter_out_keys = lambda d, x: dict((k,v) for k,v in d.iteritems() if k not in x)
cont_list = [filter_out_keys(d, filt_key_list) for d in read_in_conts]
P.S. I'd suggest making filt_key_list a set() instead - it will make in checks faster.
def filter_dict(d, keys):
return dict((key, value) for key, value in d.iteritems() if key not in filt_key_list))
cont_list = [filter_dict(d, filt_key_list) for d in read_in_conts]
You code is fine. You can make it slightly shorter:
# sets can be faster if `ignored_keys` is actually much longer
ignored_keys = set(["F2", "F5"])
# the inline version of your loop
# a dict comprehension inside a list comprehension
filtered = [{k : v for k,v in row.iteritems() if k not in ignored_keys}
for row in read_in_conts]

Python - Unflatten dict

I have this multi-dimensional dict:
a = {'a' : 'b', 'c' : {'d' : 'e'}}
And written simple function to flatten that dict:
def __flatten(self, dictionary, level = []):
tmp_dict = {}
for key, val in dictionary.items():
if type(val) == dict:
tmp_dict.update(self.__flatten(val, level + [key]))
else:
tmp_dict['.'.join(level + [key])] = val
return tmp_dict
After call this function with dict a i get in result:
{'a' : 'b', 'c.d' : 'e'}
Now, after making few instructions on this flattened dict i need to build new, multi-dimensional dict from that flattened. Example:
>> unflatten({'a' : 0, 'c.d' : 1))
{'a' : 0, 'c' : {'d' : 1}}
The only problem I have is that i do not have a function unflatten :)
Can anyone help with this? I have no idea how to do it.
EDIT:
Another example:
{'a' : 'b', 'c.d.e.f.g.h.i.j.k.l.m.n.o.p.r.s.t.u.w' : 'z'}
Should be after unflatten:
{'a': 'b', 'c': {'d': {'e': {'f': {'g': {'h': {'i': {'j': {'k': {'l': {'m': {'n': {'o': {'p': {'r': {'s': {'t': {'u': {'w': 'z'}}}}}}}}}}}}}}}}}}}
And another:
{'a' : 'b', 'c.d' : 'z', 'c.e' : 1}
To:
{'a' : 'b', 'c' : {'d' : 'z', 'e' : 1}}
This greatly increases the difficulty of the task, i know. Thats why i had problem with this and found no solution in hours..
def unflatten(dictionary):
resultDict = dict()
for key, value in dictionary.items():
parts = key.split(".")
d = resultDict
for part in parts[:-1]:
if part not in d:
d[part] = dict()
d = d[part]
d[parts[-1]] = value
return resultDict
from collections import defaultdict
def unflatten(d):
ret = defaultdict(dict)
for k,v in d.items():
k1,delim,k2 = k.partition('.')
if delim:
ret[k1].update({k2:v})
else:
ret[k1] = v
return ret
Here's one utilizing Python 3.5+ features, like typing and destructuring assignments. Try the tests out on repl.it.
from typing import Any, Dict
def unflatten(
d: Dict[str, Any],
base: Dict[str, Any] = None,
) -> Dict[str, Any]:
"""Convert any keys containing dotted paths to nested dicts
>>> unflatten({'a': 12, 'b': 13, 'c': 14}) # no expansion
{'a': 12, 'b': 13, 'c': 14}
>>> unflatten({'a.b.c': 12}) # dotted path expansion
{'a': {'b': {'c': 12}}}
>>> unflatten({'a.b.c': 12, 'a': {'b.d': 13}}) # merging
{'a': {'b': {'c': 12, 'd': 13}}}
>>> unflatten({'a.b': 12, 'a': {'b': 13}}) # insertion-order overwrites
{'a': {'b': 13}}
>>> unflatten({'a': {}}) # insertion-order overwrites
{'a': {}}
"""
if base is None:
base = {}
for key, value in d.items():
root = base
###
# If a dotted path is encountered, create nested dicts for all but
# the last level, then change root to that last level, and key to
# the final key in the path.
#
# This allows one final setitem at the bottom of the loop.
#
if '.' in key:
*parts, key = key.split('.')
for part in parts:
root.setdefault(part, {})
root = root[part]
if isinstance(value, dict):
value = unflatten(value, root.get(key, {}))
root[key] = value
return base
I wrote one years ago in Python 2 and 3 which I've adapted below. It was for making it easier to check if a given dictionary is a subset of a larger dictionary irrespective of whether provided in flattened or scaffolded form.
A bonus feature: Should there be consecutive integer indexes (as in 0, 1, 2, 3, 4 etc.), this will also convert them back into lists as well.
def unflatten_dictionary(field_dict):
field_dict = dict(field_dict)
new_field_dict = dict()
field_keys = list(field_dict)
field_keys.sort()
for each_key in field_keys:
field_value = field_dict[each_key]
processed_key = str(each_key)
current_key = None
current_subkey = None
for i in range(len(processed_key)):
if processed_key[i] == "[":
current_key = processed_key[:i]
start_subscript_index = i + 1
end_subscript_index = processed_key.index("]")
current_subkey = int(processed_key[start_subscript_index : end_subscript_index])
# reserve the remainder descendant keys to be processed later in a recursive call
if len(processed_key[end_subscript_index:]) > 1:
current_subkey = "{}.{}".format(current_subkey, processed_key[end_subscript_index + 2:])
break
# next child key is a dictionary
elif processed_key[i] == ".":
split_work = processed_key.split(".", 1)
if len(split_work) > 1:
current_key, current_subkey = split_work
else:
current_key = split_work[0]
break
if current_subkey is not None:
if current_key.isdigit():
current_key = int(current_key)
if current_key not in new_field_dict:
new_field_dict[current_key] = dict()
new_field_dict[current_key][current_subkey] = field_value
else:
new_field_dict[each_key] = field_value
# Recursively unflatten each dictionary on each depth before returning back to the caller.
all_digits = True
highest_digit = -1
for each_key, each_item in new_field_dict.items():
if isinstance(each_item, dict):
new_field_dict[each_key] = unflatten_dictionary(each_item)
# validate the keys can safely converted to a sequential list.
all_digits &= str(each_key).isdigit()
if all_digits:
next_digit = int(each_key)
if next_digit > highest_digit:
highest_digit = next_digit
# If all digits and can be sequential order, convert to list.
if all_digits and highest_digit == (len(new_field_dict) - 1):
digit_keys = list(new_field_dict)
digit_keys.sort()
new_list = []
for k in digit_keys:
i = int(k)
if len(new_list) <= i:
# Pre-populate missing list elements if the array index keys are out of order
# and the current element is ahead of the current length boundary.
while len(new_list) <= i:
new_list.append(None)
new_list[i] = new_field_dict[k]
new_field_dict = new_list
return new_field_dict
# Test
if __name__ == '__main__':
input_dict = {'a[0]': 1,
'a[1]': 10,
'a[2]': 5,
'b': 10,
'c.test.0': "hi",
'c.test.1': "bye",
"c.head.shoulders": "richard",
"c.head.knees": 'toes',
"z.trick.or[0]": "treat",
"z.trick.or[1]": "halloween",
"z.trick.and.then[0]": "he",
"z.trick.and.then[1]": "it",
"some[0].nested.field[0]": 42,
"some[0].nested.field[1]": 43,
"some[2].nested.field[0]": 44,
"mixed": {
"statement": "test",
"break[0]": True,
"break[1]": False,
}}
expected_dict = {'a': [1, 10, 5],
'b': 10,
'c': {
'test': ['hi', 'bye'],
'head': {
'shoulders': 'richard',
'knees' : 'toes'
}
},
'z': {
'trick': {
'or': ["treat", "halloween"],
'and': {
'then': ["he", "it"]
}
}
},
'some': {
0: {
'nested': {
'field': [42, 43]
}
},
2: {
'nested': {
'field': [44]
}
}
},
"mixed": {
"statement": "test",
"break": [True, False]
}}
# test
print("Input:")
print(input_dict)
print("====================================")
print("Output:")
actual_dict = unflatten_dictionary(input_dict)
print(actual_dict)
print("====================================")
print(f"Test passed? {expected_dict==actual_dict}")
As a rough-draft (could use a little improvement in variable name choice, and perhaps robustness, but it works for the example given):
def unflatten(d):
result = {}
for k,v in d.iteritems():
if '.' in k:
k1, k2 = k.split('.', 1)
v = {k2: v}
k = k1
result[k] = v
return result

Categories