2017-09-21 39 views
1

如果我的嵌套對象是字典,它會更簡單,但這些是字典列表。 實施例:將嵌套對象的列表非規範化/拼合成點分隔的鍵值對

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}, 
] 

基本上,產生扁平的物體的數目將等於(OBJ *深度)

隨着我當前的代碼:

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} 
] 

我能夠扁平化的對象(雖然不準確的),但我不能夠在每一個嵌套的對象來創建新的對象。 由於我的應用程序部署在應用程序引擎上,因此無法使用熊貓庫。

+0

我發現一個複雜的對象('dict')總是封裝在列表中。如果在輸入時會發生什麼情況,例如'「d」:{「da」:4,「db」:6}'?那可能嗎? – CristiFati

+0

不,嵌套對象將始終是列表中的對象。值始終是對象列表或字符串/數字。 – sheenu85

+0

另一個問題:如果例如輸入中的第一個對象會有更多的那個列表字段呢?例如''d「:[{」da「:1,」db「:2},{」da「:3,」db「:4}]',那會在輸出中產生4個條目呢? – CristiFati

回答

1

下面的代碼:

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評論(對於一個輸入字典具有不止一個關鍵字,其值由具有多於一個元素的列表構成),可以通過分解第e "d"all_objs。此外,在理論上它應當支持任何深度
  • flatten_dict - 採用輸入字典並輸出字典列表(作爲輸入字典可能產生多於一個的輸出字典):
    • 每個鍵具有「簡單」(不列表)的值,進入輸出dictionar(Y/IES)不變
    • 此時,一個輸出字典完成(如果輸入字典將產生比輸出詞典更多,都將具有字典鍵/值,如果它只生成一個輸出字典,那麼這將是一個)
    • 接着,用「有問題」的值的密鑰 - 其可低於輸出詞典產生更多的 - (如果有的話)進行處理:
      • 鍵的具有帶有單一元素的列表(「有問題」 ) - 每個可能產生多於一個的輸出詞典:
        • 每個都將被展平的值的(可能產生多於一個的輸出字典);將在該過程中使用相應的密鑰
        • 然後,將在所有扁平詞典列表上計算笛卡爾積(對於當前輸入,將只有一個元素具有一個列表)
        • 現在,每個產品項需要是在一個不同輸出詞典,所以字典被複制並與在商品條目每個元件的鍵/值進行更新(對於當前輸入,會有每個產品項目只有一個元素)
        • 新詞典附加到列表中
      • 此時基數列表詞典(可能只有一個)是完整的,如果沒有包含多個元素的列表組成的列表,則這是返回列表,否則下面的所有內容都必須完成每個字典在列表
      • 鑰匙具有與多個元素(「非常有問題」)的列表 - 每個產生多於一個的輸出字典:
        • 首先,笛卡爾乘積將根據所有的值(列表wi不止一個元素)。在當前情況下,因爲它只是一個這樣的列表,所以每個產品項目將只包含來自該列表的元素
        • 然後,對於每個產品項目元素,其密鑰將需要基於列表順序建立(針對電流輸入,該產品項目將只包含一種元素,而且,將僅存在一個鍵)
        • 再次,每個產品需要在一個不同輸出詞典,所以字典被複制並用扁平產品項目的鍵/值更新
      • 將新字典追加到輸出d ictionaries列表
  • 工程與Python3Python2
  • 可能很慢(尤其是對於大的輸入對象),因爲性能並不是目標。此外,由於它是自下而上(在處理新案例時增加了功能),因此它非常扭曲(RO:intortocheated),可能會有一個更簡單的實現,我錯過了。

輸出:

c:\Work\Dev\StackOverflow\q46341856>c:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe a.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}] 

@ EDIT0

  • 變得更加普遍(儘管它不是當前輸入可見):只包含值一個元素可以產生超過輸出字典(當展平時),addre ssed這種情況下,(在我只考慮1 ST輸出字典,簡單地忽略其它)
  • 修正了被掩蔽掉元組拆包與笛卡爾積組合的邏輯錯誤:if not complex_items ...部分

@ EDIT1

  • 修改代碼來匹配一個要求的變化:在展平字典中的鍵必須具有充分嵌套路徑在輸入字典
+0

{'a':11,'b.ba':22,'b.bb':33,'bc.e':2,'bc.h':1,'c':44}]這個輸出對象預期爲{'a':11,'b.ba':22,'b.bb':33,'b.bc.e':2,'b.bc.h':1, 'c':44}]所有級別的嵌套應該保持完整的路徑。 – sheenu85

+0

解決方案工作很好,只需稍作更改。非常感謝:) – sheenu85

+0

雖然它在實際輸出中,但_complete關鍵path_是預期輸出(''bc.h'')中的一個要求。我發現有點奇怪,但我認爲這是要求。無論如何,當深入到遞歸時,'parent_key'必須設置爲'new_key'而不是'key'。將相應地編輯答案。 – CristiFati

0

使用此代碼來獲得您想要的輸出。它基於遞歸調用生成輸出。

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) 

讓我知道它是否適合你。

+0

它不適用於此示例:[{'a':1,'c':[{'aa':11,'bb':22},{'aa':101,'bb':202 }], 'b':2}]
輸出出來:[ { 「一」:1, 「c.aa」:11, 「c.bb」:22 },{ 「一「:1, 」c.aa「:101, 」c.bb「:202, 」b「:2 } ],即在第一個生成的扁平物體中缺少」b「:2。 – sheenu85

+0

@Madhukar,它有時不會迭代關鍵值,而是在收集值之後進行迭代。 – sheenu85