2016-08-02 220 views
0

前言:幫助解釋爲什麼我這樣做,我將解釋最終目標。基本上,我有一個帳戶列表,這些帳戶是以非常特定的語法定義的。以下是一些示例:從Python中的多個嵌套字典/列表創建樹

Assets:Bank:Car 
Assets:Bank:House 
Assets:Savings:Emergency 
Assets:Savings:Goals:Roof 
Assets:Reserved 

如上所述,帳戶可以有任意數量的父母和子女。最終目標是將上述帳戶解析爲Python中的樹狀結構,用於在Sublime Text Editor中提供帳戶自動完成功能(例如,如果我輸入Assets:然後查詢自動完成,我會與列表呈現爲這樣:銀行,儲蓄,保留

結果:從前言使用帳戶列表,在Python期望的結果將類似於下面:

[ 
    { 
     "Assets":[ 
     { 
      "Bank":[ 
       "Car", 
       "House" 
      ] 
     }, 
     { 
      "Savings":[ 
       "Emergency", 
       { 
        "Goals":[ 
        "Roof" 
        ] 
       } 
      ] 
     }, 
     "Reserved" 
     ] 
    } 
] 

半解決方案:我wa可以使用遞歸獲得兩個基本賬戶。這適用於添加這兩個:Assets:Bank:CarAssets:Bank:House。但是,一旦他們開始有所不同,它開始崩潰,遞歸變得混亂,所以我不確定這是否是最好的方法。

import re 

def parse_account(account_str): 
    subs = account_str.split(":") 

    def separate(subs): 
     if len(subs) == 1: 
      return subs 
     elif len(subs): 
      return [{subs[0]: separate(subs[1:])}] 

    return separate(subs) 

def merge_dicts(a, b): 
    # a will be a list with dictionaries and text values and then nested lists/dictionaries/text values 
    # b will always be a list with ONE dictionary or text value 

    key = b[0].keys()[0] # this is the dictionary key of the only dictionary in the b list 

    for item in a: # item is a dictionary or a text value 
     if isinstance(item, dict): # if item is a dictionary 
      if key in item: 
       # Is the value a list with a dict or a list with a text value 
       if isinstance(b[0][key][0], str): 
        # Extend the current list with the new value 
        item[key].extend(b[0][key]) 
       else: 
        # Recurse to the next child 
        merge_dicts(item[key], b[0][key]) 
      else: 


    return a 

# Accounts have an "open [name]" syntax for defining them 
text = "open Assets:Bank:Car\nopen Assets:Bank:House\nopen Assets:Savings:Emergency\nopen Assets:Savings:Goals:Roof\nopen Assets:Reserved" 
EXP = re.compile("open (.*)") 
accounts = EXP.findall(text) # This grabs all accounts 

# Create a list of all the parsed accounts 
dicts = [] 
for account in accounts: 
    dicts.append(parse_account(account)) 

# Attempt to merge two accounts together 
final = merge_dicts(dicts[0], dicts[1]) 
print final 

# In the future we would call: reduce(merge_dicts, dicts) to merge all accounts 

我可能會以完全錯誤的方式解決這個問題,我會對不同的意見感興趣。否則,是否有人知道如何使用示例字符串中的其餘帳戶進行此項工作?

+0

你所需的輸出將給予'SyntaxError'('Reserved'是一個沒有價值的關鍵)。 – mgilson

+0

我很抱歉,我把輸出粘貼爲:'print json.dumps(final)',以使它更易於辨別。 –

回答

1

這花了我很多時間來理清我的頭腦。字典很簡單,一個鍵總是有一個列表作爲一個值 - 他們習慣於有一個命名列表。

列表內將是一個字符串或其他字典(帶有列表的鍵)。

這意味着我們可以分解'資產:銀行:汽車',並在匹配{"Assets":[<whatever>]}的根列表中查找字典或添加一個 - 然後跳轉到[<whatever>]列表中更深的兩個級別。下一個循環,查找匹配{"Bank":[<whatever>]}的字典,或者添加一個,跳到[<whatever>]列表更深的兩個級別。繼續這樣做直到我們碰到最後一個節點Car。我們必須在a列表中,因爲我們總是跳到現有列表或創建一個新列表,因此將Car放在當前列表中。

注意:這種做法將打破,如果你有

Assets:Reserved 
Assets:Reserved:Painting 

但是這將是毫無意義的衝突的輸入,要求「保留」既葉節點和容器,在這種情況下,你只會有:

Assets:Reserved:Painting 

對不對?

data = """ 
Assets:Bank:Car 
Assets:Bank:House 
Assets:Savings:Emergency 
Assets:Savings:Goals:Roof 
Assets:Reserved 
""" 
J = [] 

for line in data.split('\n'): 
    if not line: continue 

    # split the line into parts, start at the root list 
    # is there a dict here for this part? 
    # yes? cool, dive into it for the next loop iteration 
    # no? add one, with a list, ready for the next loop iteration 
    # (unless we're at the final part, then stick it in the list 
    #  we made/found in the previous loop iteration) 

    parts = line.split(':') 
    parent_list, current_list = J, J 

    for index, part in enumerate(parts): 
     for item in current_list: 
      if part in item: 
       parent_list, current_list = current_list, item[part] 
       break 
     else: 
      if index == len(parts) - 1: 
       # leaf node, add part as string 
       current_list.append(part) 
      else: 
       new_list = [] 
       current_list.append({part:new_list}) 
       parent_list, current_list = current_list, new_list  

print J 

- >

[{'Assets': [{'Bank': ['Car', 'House']}, {'Savings': ['Emergency', {'Goals': ['Roof']}]}, 'Reserved']}] 

嘗試在線:https://repl.it/Ci5L

+0

謝謝!我實際上最終得到了一個可行的解決方案,但這比我的實施稍微短了一點。另外,關於您關於葉節點和容器的問題,是的,以這種方式定義帳戶是無效的。 –