Suppose I have a dictionary, and it's nested with dictionaries inside. I want to join all the values of that dictionary, recursively?
' '.join(d.values())
That works if there are no nests.
The following works for any non-recursive nested dicts:
def flatten_dict_values(d):
values = []
for value in d.itervalues():
if isinstance(value, dict):
values.extend(flatten_dict_values(value))
else:
values.append(value)
return values
>>> " ".join(flatten_dict_values({'one': 'not-nested',
... 'two': {'three': 'nested',
... 'four': {'five': 'double-nested'}}}))
'double-nested nested not-nested'
Edit: Support for recursive dicts
If you need to support self-referencing dicts, you need to extend the above code to keep track of all processed dicts and make sure you never attempt to process a dictionary that you've already seen. The following is a reasonably cheap, yet readable way to do it:
def flatten_dict_values(d, seen_dict_ids=None):
values = []
seen_dict_ids = seen_dict_ids or set()
seen_dict_ids.add(id(d))
for value in d.itervalues():
if id(value) in seen_dict_ids:
continue
elif isinstance(value, dict):
values.extend(flatten_dict_values(value, seen_dict_ids))
else:
values.append(value)
return values
>>> recursive_dict = {'one': 'not-nested',
... 'two': {'three': 'nested'}}
>>> recursive_dict['recursive'] = recursive_dict
>>> " ".join(flatten_dict_values(recursive_dict))
'nested not-nested'
Try something along
def flatten(d):
ret = []
for v in d.values():
if isinstance(v, dict):
ret.extend(flatten(v))
else:
ret.append(v)
return ret
my_dict = {1: 'bar', 5: {6: 'foo', 7: {'cat': 'bat'}}}
assert ' '.join(flatten(my_dict)) == 'bar foo bat'
This would work with nested iterables of other sort than dict as well if configured right:
: :
def should_iter_fnc(it):
"""returns 'True' if 'it' is viewed as nested"""
raise NotImplementedError
def join_fnc(itr):
"""transform an iterable 'itr' into appropriate object"""
raise NotImplementedError
def values_fnc(itr):
"""get the list of 'values' of interest from iterable 'itr'"""
raise NotImplementedError
def recursive_join(smth, join_fnc, should_iter_fnc, values_fnc):
if should_iter_fnc(smth):
return join_fnc([
recursive_join(it, join_fnc, should_iter_fnc, values_fnc)
for it in values_fnc(smth)
])
else:
return smth
giving:
>>>
def should_iter(it):
"""Returns 'True', if 'it' is 'iterable' but not an 'str' instance"""
if isinstance(it, str):
return False
try:
iter(it)
return True
except TypeError:
return False
>>> print recursive_join(smth=[['1','2'],['3','4'],'5'],
join_fnc=lambda itr: ' '.join(itr),
should_iter_fnc=should_iter,
values_fnc=list)
1 2 3 4 5
>>> print recursive_join(smth={1:{1:'1',2:'2'},2:{3:'3',4:'4'},3:'5'},
join_fnc=lambda itr: ' '.join(itr),
should_iter_fnc=should_iter,
values_fnc=lambda dct:dct.values())
1 2 3 4 5
Map/Reduce is a common way for this type of problem.
Example for a two level nested dictionary:
>>> nested_dicts = {'one': {'one_one': 'one_one_value', 'one_two': 'one_two_value'}, 'two': {'two_one': 'two_one_value', 'two_two': 'two_two_value'}}
>>> from functools import reduce
>>> reduce(lambda x, y: x + list(y.values()), nested_dicts.values(), [])
['one_two_value', 'one_one_value', 'two_two_value', 'two_one_value']
You can, of course, use recursion with reduce
, as well:
>>> nested_dicts = {'d1': 'd1 val', 'd2': {'d2_1': {'d2_1_1': 'd2_1_1 val', 'd2_1_2': 'd2_1_2 val'}, 'd2_2': {'d2_2_1': 'd2_2_1 val'}}}
>>> def to_list(value):
... return [value] if isinstance(value, str) else reduce(lambda x,y: x+to_list(y), value.values(), [])
...
>>> to_list('test')
['test']
>>> reduce(lambda x, y: x+to_list(y), nested_dicts.values(), [])
['d2_2_1 val', 'd2_1_1 val', 'd2_1_2 val', 'd1 val']
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.