2010-03-29 3 views
4

Предположим, у меня есть словарь, и он вложен в словари внутри. Я хочу объединить все значения этого словаря, рекурсивно?Как присоединиться к значениям вложенного словаря Python?

' '.join(d.values()) 

Это работает, если нет гнезд.

+0

Что делать, если это бесконечные вложенные словари? Я не знаю, сколько уровней. – TIMEX

+0

Мой обновленный ответ поддерживает как вложенные, так и рекурсивные словари с обнаружением цикла для предотвращения бесконечной рекурсии. –

+2

Произвольное гнездование dicts, скорее всего, указывает на конструкцию, которая может быть улучшена. –

ответ

7

следующие работы для любой не-рекурсивной вложенной 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: Поддержка для рекурсивного dicts

Если вам необходимо поддерживать автореферентный dicts, вам необходимо расширить выше кода для отслеживания всех обработанных dicts и убедитесь, что вы никогда не пытаетесь обработать словарь, который вы уже видели. Ниже достаточно дешевый, но читаемый способ сделать это:

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' 
4

Попробуйте что-то вдоль

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' 
1

Это будет работать с вложенными итерируемыми другого рода, чем Словаре, а также если они настроены правильно:

'Прототипы':

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 

дает:

>>> 
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 
0

Map/Reduce является распространенным способом для этого типа проблемы.

Пример для вложенного словаря два уровня:

>>> 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'] 

Вы можете, конечно, использовать рекурсию с reduce, а также:

>>> 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'] 
Смежные вопросы