代码之家  ›  专栏  ›  技术社区  ›  sheenu85

将嵌套对象列表反规范化/展平为点分隔的键值对

  •  2
  • sheenu85  · 技术社区  · 7 年前

    例子:

    all_objs1 = [{
        'a': 1,
        'b': [{'ba': 2, 'bb': 3}, {'ba': 21, 'bb': 31}],
        'c': 4
    }, {
        'a': 11,
        'b': [{'ba': 22, 'bb': 33, 'bc': [{'h': 1, 'e': 2}]}],
        'c': 44
    }]
    

    [
      {'a': 1, 'b.ba': 2, 'b.bb': 3, 'c': 4},
      {'a': 1, 'b.ba': 21, 'b.bb': 31, 'c': 4},
      {'a': 11, 'b.ba': 22, 'b.bb': 33, 'bc.h': 1, 'bc.e': 2, 'c': 44},
    ]
    

    def flatten(obj, flattened_obj, last_key=''):
      for k,v in obj.iteritems():
        if not isinstance(v, list):
          flattened_obj.update({last_key+k : v})
        else:
          last_key += k + '.'
          for nest_obj in v:
            flatten(nest_obj, flattened_obj, last_key)
            last_key = remove_last_key(last_key)
    
    def remove_last_key(key_path):
        second_dot = key_path[:-1].rfind('.')
        if second_dot > 0:
          return key_path[:second_dot+1]
        return key_path
    

    输出:


    [
      {'a': 1, 'b.bb': 31, 'c': 4, 'b.ba': 21},
      {'a': 11, 'b.bc.e': 2, 'c': 44, 'b.bc.h': 1, 'b.bb': 33, 'b.ba': 22}
    ]
    

    我能够展平对象(虽然不准确),但我无法在每个嵌套对象上创建新对象。 我不能使用熊猫库,因为我的应用程序部署在应用程序引擎上。

    2 回复  |  直到 7 年前
        1
  •  3
  •   CristiFati    6 年前

    密码py公司 :

    from itertools import product
    from pprint import pprint as pp
    
    
    all_objs = [{
        "a": 1,
        "b": [{"ba": 2, "bb": 3}, {"ba": 21, "bb": 31}],
        "c": 4,
        #"d": [{"da": 2}, {"da": 5}],
    }, {
        "a": 11,
        "b": [{"ba": 22, "bb": 33, "bc": [{"h": 1, "e": 2}]}],
        "c": 44,
    }]
    
    
    def flatten_dict(obj, parent_key=None):
        base_dict = dict()
        complex_items = list()
        very_complex_items = list()
        for key, val in obj.items():
            new_key = ".".join((parent_key, key)) if parent_key is not None else key
            if isinstance(val, list):
                if len(val) > 1:
                    very_complex_items.append((key, val))
                else:
                    complex_items.append((key, val))
            else:
                base_dict[new_key] = val
        if not complex_items and not very_complex_items:
            return [base_dict]
        base_dicts = list()
        partial_dicts = list()
        for key, val in complex_items:
            partial_dicts.append(flatten_dict(val[0], parent_key=new_key))
        for product_tuple in product(*tuple(partial_dicts)):
            new_base_dict = base_dict.copy()
            for new_dict in product_tuple:
                new_base_dict.update(new_dict)
            base_dicts.append(new_base_dict)
        if not very_complex_items:
            return base_dicts
        ret = list()
        very_complex_keys = [item[0] for item in very_complex_items]
        very_complex_vals = tuple([item[1] for item in very_complex_items])
        for product_tuple in product(*very_complex_vals):
            for base_dict in base_dicts:
                new_dict = base_dict.copy()
                new_items = zip(very_complex_keys, product_tuple)
                for key, val in new_items:
                    new_key = ".".join((parent_key, key)) if parent_key is not None else key
                    new_dict.update(flatten_dict(val, parent_key=new_key)[0])
                ret.append(new_dict)
        return ret
    
    
    def main():
        flatten = list()
        for obj in all_objs:
            flatten.extend(flatten_dict(obj))
        pp(flatten)
    
    
    if __name__ == "__main__":
        main()
    

    :

    • 正如预期的那样,使用了递归
    • 这是一般的,它也适用于我在我的2 nd公司 注释(对于具有多个键且值由具有多个元素的列表组成的输入dict),可以通过对 “d” 输入 . 此外,理论上它应该支持任何深度
    • 展平dict :获取输入字典并输出字典列表(因为输入字典可能会生成多个输出字典):
      • 每个键都有一个“简单”(非列表)值,不变地进入输出字典
      • 在这一点上 基础 输出字典已完成(如果输入字典将生成多个输出字典,则所有字典都将具有 字典键/值,如果它只生成一个输出字典,那么这将是 基础 一)
        • 具有单个元素列表的键(“有问题”)-每个 可以
          • 然后,将在所有展平字典列表上计算笛卡尔乘积(对于当前输入,只有一个列表具有一个元素)
          • 输出字典,因此 基础 每一个
        • 在这一点上 基础 字典(可能只有一个)已完成,如果没有包含多个元素的列表的值,则这是返回列表,否则必须执行以下所有操作 每个 列表中的词典
        • 生成多个输出字典:
          • 然后,对于每个产品项元素,需要根据列表顺序建立其键(对于当前输入,产品项将只包含一个元素,并且只有一个键)
          • 不同的 输出字典,因此 基础 复制字典,并使用展平产品项的键/值进行更新
        • 新词典将附加到输出词典列表中
    • 使用 Python 2
    • INTORTO受骗 :)),可能有一个更简单的实现,我错过了。

    输出

    c:\Work\Dev\StackOverflow\q046341856>c:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe code.py
    [{'a': 1, 'b.ba': 2, 'b.bb': 3, 'c': 4},
     {'a': 1, 'b.ba': 21, 'b.bb': 31, 'c': 4},
     {'a': 11, 'b.ba': 22, 'b.bb': 33, 'b.bc.e': 2, 'b.bc.h': 1, 'c': 44}]
    

    @编辑0 :

    • 纠正了一个逻辑错误,该错误被元组解包和笛卡尔乘积掩盖了: if not complex_items ... 部分

    :

    • 完整嵌套路径 在输入字典中
        2
  •  0
  •   P.Madhukar    7 年前

    使用此代码可以获得所需的输出。它基于递归调用生成输出。

    import json
    from copy import deepcopy
    def flatten(final_list, all_obj, temp_dct, last_key):
    
    
        for dct in all_obj:
            deep_temp_dct = deepcopy(temp_dct)
            for k, v in dct.items():
                if isinstance(v, list):
                    final_list, deep_temp_dct = flatten(final_list, v, deep_temp_dct, k)
                else:
                    prefix = ""
                    if last_key : prefix = last_key + "."
                    key = prefix+ k
                    deep_temp_dct[key] = v
            if deep_temp_dct not in final_list:
                final_list.append(deep_temp_dct)
    
        return final_list, deep_temp_dct
    
    final_list, _ = flatten([], all_objs1, {}, "")
    print json.dumps(final_list, indent =4 )
    

    如果对你有效,请告诉我。