python - 在 Python 中展平通用 JSON 字典列表或列表

标签 python json list dictionary recursive-datastructures

我有一组任意的 JSON 数据,这些数据已在 Python 中解析为字典列表和不同深度的列表。我需要能够将其“扁平化”为字典列表。示例如下:

源数据示例 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'},
]

对于这个简单的例子来说这已经足够简单了,但我并不总是有这种精确的字典列表结构,还有一层额外的字典列表。在某些情况下,我可能有额外的嵌套需要遵循相同的方法。因此,我认为我需要递归,但我似乎无法让它发挥作用。

提议的方法论

1) 对于每个字典列表,在每个键前加上一个提供父键名称的“路径”。在上面的示例中,“industry”是包含字典列表的键,因此列表中的每个子字典都添加了“industry”。

2) 向列表中的每个字典添加“父”项 - 在这种情况下,“名称”和“行业”是顶级字典列表中的项,因此添加了“名称”键/值“行业”中的每一项。

我可以想象一些场景,您在“父”项中有多个字典列表,甚至是字典的字典,并且将这些子树中的每一个应用于子字典列表是行不通的。因此,我假设“父”项始终是简单的键/值对。

再举一个例子来说明需要处理的数据结构中的潜在变化。

源数据示例 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 矩阵 - 显然我遇到了递归失控的麻烦。

我已经尝试了一些额外的“从头开始”尝试,但都以类似的失败模式失败了。

最佳答案

好的。我的解决方案有两个功能。第一个是 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_idindustry_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'}]

关于python - 在 Python 中展平通用 JSON 字典列表或列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21512957/

相关文章:

python - Webdriver + PhantomJS 只是卡在那里

javascript - AWS Lambda - Nodejs 函数不会返回数据

python - 错误列表索引必须是整数或切片,而不是 str

java - 使用 mongodb 查找,同时使用 java 中的吗啡迭代列表

python - 无法通过 Mac High Sierra 上的 gi.repository 在 python 中导入 WebKit

python - 使用列表理解

json - Curl 从输出中获取特定值

javascript - 从 JSON 文件解析 HTML 时 ngResource 不起作用

python - 如果元素包含 '>",则在列表中插入一个字符

python - 如何使用python获取wlan设备名称?