对于某些后处理,我需要展平像这样的结构
{'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
)?
答案 0 :(得分: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
)的情况下合并字典。