2017-08-07 88 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)