2017-08-07 87 views
2

簡單的Python問題,但我正在摸索着回答問題!Python:檢索任意字典路徑並修改數據?

我有稱爲path任意長度的字符串數組,像這樣:

path = ['country', 'city', 'items'] 

我也有一個字典,data,和一個字符串,unwanted_property。我知道字典具有任意深度,並且字典一直向下,除了items屬性,它總是一個數組。

[澄清:這個問題的關鍵是我不知道path的內容是什麼。他們可能是任何東西。我也不知道字典會是什麼樣子。我需要沿着路徑指示走下字典,然後從那裏刪除不需要的屬性,而不事先知道路徑的樣子,或者它將會有多長。]

我想檢索與path匹配的數據對象的部分(如果有),然後從每個對象中刪除unwanted_property

因此,在上面的例子中,我想檢索:

data['country']['city']['items'] 
從每個陣列中的項目的

,然後刪除unwanted_property。我想修改原始數據,而不是副本。 (澄清:我的意思是,我想用最初的字典結尾,只是減去不需要的屬性。)

我該如何在代碼中執行此操作?

我走了這麼遠:

path = ['country', 'city', 'items'] 
data = { 
    'country': { 
     'city': { 
      'items': [ 
       { 
        'name': '114th Street', 
        'unwanted_property': 'foo', 
       }, 
       { 
        'name': '8th Avenue', 
        'unwanted_property': 'foo', 
       }, 
      ] 
     } 
    } 
} 
for p in path: 
    if p == 'items': 
     data = [i for i in data[p]] 
    else: 
     data = data[p] 
if isinstance(data, list): 
    for d in data: 
     del d['unwanted_property'] 
else: 
    del data['unwanted_property'] 

的問題是,這並沒有修改原始數據。它也依賴於items始終是路徑中的最後一個字符串,這可能並非總是如此。

澄清:我的意思是,我想直到結束:

{ 
    'country': { 
     'city': { 
      'items': [ 
       { 
        'name': '114th Street' 
       }, 
       { 
        'name': '8th Avenue' 
       }, 
      ] 
     } 
    } 
} 

而我在data有可用的只有[{'name': '114th Street'}, {'name': '8th Avenue'}]

我覺得我需要像XPath這樣的字典。

+0

當你說它不修改原始數據,你的意思是? '數據'沒有改變? –

+0

@COLDSPEED我的意思是'data'現在只有'[{'name':'114th Street'},{'name':'8th Avenue'}]'而我希望它是完整的字典,只是減去不需要的財產。 – Richard

+0

將一個新的變量'temp'指定給'data',然後用'temp'完成確切的事情。 –

回答

1

問題要覆蓋原data參考。你的處理代碼更改爲

 
temp = data 
for p in path: 
temp = temp[p] 
if isinstance(temp, list): 
    for d in temp: 
     del d['unwanted_property'] 
else: 
    del temp['unwanted_property'] 

在這個版本中,您將temp爲指向data指的是同一個對象。 temp不是副本,因此您對其進行的任何更改都將在原始對象中可見。然後沿着它自己步驟temp,而data仍然是對根詞典的引用。當您找到要查找的路徑時,通過temp所做的任何更改將在data中可見。

我也刪除了行data = [i for i in data[p]]。它會創建一個你不需要的不必要的副本,因爲你沒有修改存儲在列表中的引用,只是引用的內容。

path不是預先確定的事實(除了一個事實,即items將是一個list)意味着你最終可能在第一循環得到一個KeyError如果路徑沒有在你的字典中。您可以處理優雅做更多的東西一樣:

try: 
    temp = data 
    for p in path: 
     temp = temp[p] 
except KeyError: 
    print('Path {} not in data'.format(path)) 
else: 
    if isinstance(temp, list): 
     for d in temp: 
      del d['unwanted_property'] 
    else: 
     del temp['unwanted_property'] 
+0

謝謝。對不起,我已經非常糟糕地解釋了這個問題。重點是我不知道'path'的內容會提前。 – Richard

+0

@Richard。沒關係。在temp [p]不存在的時刻你會得到一個'KeyError'。 –

+0

@Richard。我已經更新了一種明確處理這種情況的技術。 –

0
def delKey(your_dict,path): 
    if len(path) == 1: 
     for item in your_dict: 
      del item[path[0]] 
     return 
    delKey( your_dict[path[0]],path[1:]) 

data 
{'country': {'city': {'items': [{'name': '114th Street', 'unwanted_property': 'foo'}, {'name': '8th Avenue', 'unwanted_property': 'foo'}]}}} 
path 
['country', 'city', 'items', 'unwanted_property'] 

delKey(data,path) 

data 
{'country': {'city': {'items': [{'name': '114th Street'}, {'name': '8th Avenue'}]}}} 
0

您需要刪除密鑰unwanted_property

names_list = [] 

def remove_key_from_items(data): 
    for d in data: 
     if d != 'items': 
      remove_key_from_items(data[d]) 
     else: 
      for item in data[d]: 
       unwanted_prop = item.pop('unwanted_property', None) 
       names_list.append(item) 

這將刪除密鑰。如果密鑰unwanted_property不存在,則返回第二個參數None

編輯: 即使沒有第二個參數,也可以使用pop。如果密鑰不存在,它將提高KeyError

編輯2:更新遞歸進入data字典的深度,直到它找到items項,其中根據需要並追加到names_list列表以獲得所需的輸出它彈出的unwanted_property

+0

謝謝。對不起,我已經非常糟糕地解釋了這個問題。重點是我不知道'path'的內容會提前。 – Richard

+0

我已經編輯了答案,這將從您爲循環潛入的任何深度彈出unwanted_property鍵。 –

+0

這將*不*做問題的所有問題。它不會深入到外部字典的深處。 –

-1

你可以試試這個:

path = ['country', 'city', 'items'] 
previous_data = data[path[0]] 
previous_key = path[0] 
for i in path: 
    previous_data = previous_data[i] 
    previous_key = i 
    if isinstance(previous_data, list): 
      for c, b in enumerate(previous_data): 
       if "unwanted_property" in b: 
        del previous_data[c]["unwanted_property"] 

current_dict = {} 
previous_data_dict = {} 
for i, a in enumerate(path): 
    if i == 0: 
     current_dict[a] = data[a] 
     previous_data_dict = data[a] 
    else: 
     if a == previous_key: 
      current_dict[a] = previous_data 
     else: 
      current_dict[a] = previous_data_dict[a] 
      previous_data_dict = previous_data_dict[a] 
data = current_dict 

print(data) 

輸出:

{'country': {'city': {'items': [{'name': '114th Street'}, {'name': '8th Avenue'}]}}, 'items': [{'name': '114th Street'}, {'name': '8th Avenue'}], 'city': {'items': [{'name': '114th Street'}, {'name': '8th Avenue'}]}} 
+0

謝謝。對不起,我已經非常糟糕地解釋了這個問題。重點是我不知道'path'的內容會提前。 – Richard

+0

這不是很靈活。爲什麼不引用data [「country」] [「city」] [「items」]',然後重複這個長度? –

+0

@MadPhysicist編輯。 – Ajax1234

0

你所面臨的問題是,你正在重新分配data變量不期望的價值。在你for循環要設置data到下一個級別的樹體,比如給你的例子data將具有以下值(按順序),同比當它離開for環路:

data == {'country': {'city': {'items': [{'name': '114th Street', 'unwanted_property': 'foo',}, {'name': '8th Avenue', 'unwanted_property': 'foo',},]}}} 

data == {'city': {'items': [{'name': '114th Street', 'unwanted_property': 'foo',}, {'name': '8th Avenue', 'unwanted_property': 'foo',},]}} 

data == {'items': [{'name': '114th Street', 'unwanted_property': 'foo',}, {'name': '8th Avenue', 'unwanted_property': 'foo',},]} 

data == [{'name': '114th Street', 'unwanted_property': 'foo',}, {'name': '8th Avenue', 'unwanted_property': 'foo',},] 

然後,當您從最終字典中刪除項目時,data就是這些字典的列表,因爲您丟失了結構的更高部分。因此,如果您爲您的數據備份參考,你可以得到正確的輸出,例如:

path = ['country', 'city', 'items'] 
data = { 
    'country': { 
     'city': { 
      'items': [ 
       { 
        'name': '114th Street', 
        'unwanted_property': 'foo', 
       }, 
       { 
        'name': '8th Avenue', 
        'unwanted_property': 'foo', 
       }, 
      ] 
     } 
    } 
} 

data_ref = data 

for p in path: 
    if p == 'items': 
     data = [i for i in data[p]] 
    else: 
     data = data[p] 
if isinstance(data, list): 
    for d in data: 
     del d['unwanted_property'] 
else: 
    del data['unwanted_property'] 

data = data_ref 
+0

從技術上講,你最好只用'data_ref'而不是'data'。問題在於,你可能最終會在第一個循環中得到一個'KeyError',並且無法恢復你的數據。我的回答已經表明這樣做。 –

+0

的確,我只是簡單地試着對這個例子中的代碼做最小的修改,當你的答案上升時顯然是忙着寫這個,所以我沒有注意到。 –

0

使用operator.itemgetter您可以編寫一個函數來返回最後一個關鍵的價值。

import operator, functools 
def compose(*functions): 
    '''returns a callable composed of the functions 

    compose(f, g, h, k) -> f(g(h(k()))) 
    ''' 
    def compose2(f, g): 
     return lambda x: f(g(x)) 
    return functools.reduce(compose2, functions, lambda x: x) 

get_items = compose(*[operator.itemgetter(key) for key in path[::-1]]) 

然後使用它是這樣的:如果路徑中包含不存在的鍵

path = ['country', 'city', 'items'] 
unwanted_property = 'unwanted_property' 

for thing in get_items(data): 
    del thing[unwanted_property] 

當然它會拋出一個KeyError異常 - 你或許應該考慮的是:

path = ['country', 'foo', 'items'] 
get_items = compose(*[operator.itemgetter(key) for key in path[::-1]]) 
try: 
    for thing in get_items(data): 
     del thing[unwanted_property] 
except KeyError as e: 
    print('missing key:', e)