2016-05-24 2 views
1

Для некоторых пост-обработки, мне нужно, чтобы сгладить структуру, как этотСвести вложенную структуру Dict в наборе данных

{'foo': { 
      'cat': {'name': 'Hodor', 'age': 7}, 
      'dog': {'name': 'Mordor', 'age': 5}}, 
'bar': { 'rat': {'name': 'Izidor', 'age': 3}} 
} 

в этом наборе:

[{'foobar': 'foo', 'animal': 'dog', 'name': 'Mordor', 'age': 5}, 
{'foobar': 'foo', 'animal': 'cat', 'name': 'Hodor', 'age': 7}, 
{'foobar': 'bar', 'animal': 'rat', 'name': 'Izidor', 'age': 3}] 

Так что я написал эту функцию:

def flatten(data, primary_keys): 
    out = [] 
    keys = copy.copy(primary_keys) 
    keys.reverse() 
    def visit(node, primary_values, prim): 
     if len(prim): 
      p = prim.pop() 
      for key, child in node.iteritems(): 
       primary_values[p] = key 
       visit(child, primary_values, copy.copy(prim)) 
     else: 
      new = copy.copy(node) 
      new.update(primary_values) 
      out.append(new) 
    visit(data, { }, keys) 
    return out 

out = flatten(a, ['foo', 'bar']) 

Я был не очень доволен, потому что мне нужно использовать copy.copy для защиты моих входов. Очевидно, что при использовании flatten не требуется, чтобы входы были изменены.

Затем я подумал об одной альтернативе, которая использует более глобальные переменные (по крайней мере глобальные до flatten) и использует индекс вместо прямого прохождения primary_keys до visit. Однако это не помогает мне избавиться от уродливых первоначальной копии:

keys = copy.copy(primary_keys) 
    keys.reverse() 

Так вот мой окончательный вариант:

def flatten(data, keys): 
    data = copy.copy(data) 
    keys = copy.copy(keys) 
    keys.reverse() 
    out = [] 
    values = {} 
    def visit(node, id): 
     if id: 
      id -= 1 
      for key, child in node.iteritems(): 
       values[keys[id]] = key 
       visit(child, id) 
     else: 
      node.update(values) 
      out.append(node) 
    visit(data, len(keys)) 
    return out  

Есть ли более эффективное осуществление (что позволяет избежать использования copy.copy)?

ответ

1

Редактировать: модифицировано для учета переменной глубины словаря.

Используя функцию merge из моего предыдущего ответа (см. Ниже), вы можете избежать вызова update, который изменяет вызывающего абонента. Тогда нет необходимости скопировать словарь первым.

def flatten(data, keys): 
    out = [] 
    values = {} 
    def visit(node, id): 
     if id: 
      id -= 1 
      for key, child in node.items(): 
       values[keys[id]] = key 
       visit(child, id) 
     else: 
      out.append(merge(node, values)) # use merge instead of update 
    visit(data, len(keys)) 
    return out  

Одна вещь, которую я не понимаю, почему вы должны защитить keys вход. Я не вижу, чтобы они менялись где угодно.


Предыдущий ответ

Как насчет списка понимания?

def merge(d1, d2): 
    return dict(list(d1.items()) + list(d2.items())) 

[[merge({'foobar': key, 'animal': sub_key}, sub_sub_dict) 
    for sub_key, sub_sub_dict in sub_dict.items()] 
     for key, sub_dict in a.items()] 

Сложная часть была объединения словарей без использования update (который возвращает None).

+0

Ваше решение работает с моим образцом, но не с моими настоящими данными, которые имеют гораздо больше слоев. Мне нужен рекурсивный алгоритм. – nowox

Смежные вопросы