我有一组任意JSON数据,这些数据已在Python中解析为dicts列表和不同深度的列表。我需要能够“扁平化”。这成了一个dicts列表。示例如下:
来源数据示例1
[{u'industry': [
{u'id': u'112', u'name': u'A'},
{u'id': u'132', u'name': u'B'},
{u'id': u'110', u'name': u'C'},
],
u'name': u'materials'},
{u'industry': {u'id': u'210', u'name': u'A'},
u'name': u'conglomerates'}
]
所需结果示例1
[{u'name':u'materials', u'industry_id':u'112', u'industry_name':u'A'},
{u'name':u'materials', u'industry_id':u'132', u'industry_name':u'B'},
{u'name':u'materials', u'industry_id':u'110', u'industry_name':u'C'},
{u'name':u'conglomerates', u'industry_id':u'210', u'industry_name':u'A'},
]
对于这个简单的例子来说这很容易,但我并不总是有这个列表的确切结构,还有一层额外的dicts列表。在某些情况下,我可能有额外的嵌套需要遵循相同的方法。因此,我认为我需要递归,而我似乎无法使其发挥作用。
建议的方法
1)对于每个Dicts列表,在每个键前面添加一个'路径'提供父键的名称。在上面的示例中,'行业'是一个包含一系列词汇的关键,因此列表中的每个孩子都有“行业”和“行业”。添加到他们。
2)添加父母'列表中每个词典的项目 - 在这种情况下,'名称'和'行业'是最高级别的dicts列表中的项目,因此' name'键/值已添加到行业'
中的每个项目中我可以想象一些情况,你有多个词典列表,甚至是“父母”中的词汇序列。项目和将这些子树中的每一个应用于子项列表都不起作用。因此,我会假设父母'项目总是简单的键/值对。
另一个例子,试图说明需要处理的数据结构中的潜在变化。
来源数据示例2
[{u'industry': [
{u'id': u'112', u'name': u'A'},
{u'id': u'132', u'name': u'B'},
{u'id': u'110', u'name': u'C', u'company': [
{u'id':'500', u'symbol':'X'},
{u'id':'502', u'symbol':'Y'},
{u'id':'504', u'symbol':'Z'},
]
},
],
u'name': u'materials'},
{u'industry': {u'id': u'210', u'name': u'A'},
u'name': u'conglomerates'}
]
所需结果示例2
[{u'name':u'materials', u'industry_id':u'112', u'industry_name':u'A'},
{u'name':u'materials', u'industry_id':u'132', u'industry_name':u'B'},
{u'name':u'materials', u'industry_id':u'110', u'industry_name':u'C',
u'company_id':'500', u'company_symbol':'X'},
{u'name':u'materials', u'industry_id':u'110', u'industry_name':u'C',
u'company_id':'502', u'company_symbol':'Y'},
{u'name':u'materials', u'industry_id':u'110', u'industry_name':u'C',
u'company_id':'504', u'company_symbol':'Z'},
{u'name':u'conglomerates', u'industry_id':u'210', u'industry_name':u'A'},
]
我看了几个其他的例子,我似乎无法找到适用于这些例子的例子。
有任何建议或指示吗?我花了一些时间试图建立一个递归函数来处理这个问题,但在几个小时之后没有运气......
在一次失败的情况下更新
def _flatten(sub_tree, flattened=[], path="", parent_dict={}, child_dict={}):
if type(sub_tree) is list:
for i in sub_tree:
flattened.append(_flatten(i,
flattened=flattened,
path=path,
parent_dict=parent_dict,
child_dict=child_dict
)
)
return flattened
elif type(sub_tree) is dict:
lists = {}
new_parent_dict = {}
new_child_dict = {}
for key, value in sub_tree.items():
new_path = path + '_' + key
if type(value) is dict:
for key2, value2 in value.items():
new_path2 = new_path + '_' + key2
new_parent_dict[new_path2] = value2
elif type(value) is unicode:
new_parent_dict[key] = value
elif type(value) is list:
lists[new_path] = value
new_parent_dict.update(parent_dict)
for key, value in lists.items():
for i in value:
flattened.append(_flatten(i,
flattened=flattened,
path=key,
parent_dict=new_parent_dict,
)
)
return flattened
我得到的结果是231x231矩阵的“无”#39;对象 - 很明显我在递归的过程中遇到了麻烦。
我已经尝试了一些额外的'从头开始'尝试并以类似的故障模式失败。
答案 0 :(得分:4)
好的。我的解决方案有两个功能。第一个splitObj
负责将对象拆分为平面数据和子列表或子对象,稍后需要递归。第二个flatten
实际上迭代了一个对象列表,进行递归调用并负责为每次迭代重建最终对象。
def splitObj (obj, prefix = None):
'''
Split the object, returning a 3-tuple with the flat object, optionally
followed by the key for the subobjects and a list of those subobjects.
'''
# copy the object, optionally add the prefix before each key
new = obj.copy() if prefix is None else { '{}_{}'.format(prefix, k): v for k, v in obj.items() }
# try to find the key holding the subobject or a list of subobjects
for k, v in new.items():
# list of subobjects
if isinstance(v, list):
del new[k]
return new, k, v
# or just one subobject
elif isinstance(v, dict):
del new[k]
return new, k, [v]
return new, None, None
def flatten (data, prefix = None):
'''
Flatten the data, optionally with each key prefixed.
'''
# iterate all items
for item in data:
# split the object
flat, key, subs = splitObj(item, prefix)
# just return fully flat objects
if key is None:
yield flat
continue
# otherwise recursively flatten the subobjects
for sub in flatten(subs, key):
sub.update(flat)
yield sub
请注意,这并不能完全产生您想要的输出。原因是你的输出实际上是不一致的。在第二个示例中,对于存在嵌套在行业中的公司的情况,嵌套在输出中不可见。相反,我的输出会生成industry_company_id
和industry_company_symbol
:
>>> ex1 = [{u'industry': [{u'id': u'112', u'name': u'A'},
{u'id': u'132', u'name': u'B'},
{u'id': u'110', u'name': u'C'}],
u'name': u'materials'},
{u'industry': {u'id': u'210', u'name': u'A'}, u'name': u'conglomerates'}]
>>> ex2 = [{u'industry': [{u'id': u'112', u'name': u'A'},
{u'id': u'132', u'name': u'B'},
{u'company': [{u'id': '500', u'symbol': 'X'},
{u'id': '502', u'symbol': 'Y'},
{u'id': '504', u'symbol': 'Z'}],
u'id': u'110',
u'name': u'C'}],
u'name': u'materials'},
{u'industry': {u'id': u'210', u'name': u'A'}, u'name': u'conglomerates'}]
>>> pprint(list(flatten(ex1)))
[{'industry_id': u'112', 'industry_name': u'A', u'name': u'materials'},
{'industry_id': u'132', 'industry_name': u'B', u'name': u'materials'},
{'industry_id': u'110', 'industry_name': u'C', u'name': u'materials'},
{'industry_id': u'210', 'industry_name': u'A', u'name': u'conglomerates'}]
>>> pprint(list(flatten(ex2)))
[{'industry_id': u'112', 'industry_name': u'A', u'name': u'materials'},
{'industry_id': u'132', 'industry_name': u'B', u'name': u'materials'},
{'industry_company_id': '500',
'industry_company_symbol': 'X',
'industry_id': u'110',
'industry_name': u'C',
u'name': u'materials'},
{'industry_company_id': '502',
'industry_company_symbol': 'Y',
'industry_id': u'110',
'industry_name': u'C',
u'name': u'materials'},
{'industry_company_id': '504',
'industry_company_symbol': 'Z',
'industry_id': u'110',
'industry_name': u'C',
u'name': u'materials'},
{'industry_id': u'210', 'industry_name': u'A', u'name': u'conglomerates'}]