【问题标题】:Search for a value in a nested dictionary python在嵌套字典python中搜索值
【发布时间】:2014-03-04 03:32:37
【问题描述】:

搜索一个值并获取父字典名称(键):

Dictionary = {dict1:{
        'part1': {
            '.wbxml': 'application/vnd.wap.wbxml',
            '.rl': 'application/resource-lists+xml',    
        },
        'part2':
            {'.wsdl': 'application/wsdl+xml',
            '.rs': 'application/rls-services+xml',
            '.xop': 'application/xop+xml',
            '.svg': 'image/svg+xml',
            },
        'part3':{...}, ...

   dict2:{
          'part1': {    '.dotx': 'application/vnd.openxmlformats-..'                           
            '.zaz': 'application/vnd.zzazz.deck+xml',
            '.xer': 'application/patch-ops-error+xml',}  
          },
          'part2':{...},
          'part3':{...},...  

    },...

在上面的字典中,我需要搜索以下值:"image/svg+xml"。其中,字典中没有任何值重复。如何搜索"image/svg+xml"?所以它应该返回字典{ dict1:"part2" }中的父键。

请注意:解决方案未修改适用于Python 2.7 和 Python 3.3。

【问题讨论】:

  • 请不要使用矛盾的标签...选择其中一个。
  • @aj8uppal - 这是因为,我在两个 python 版本中都没有答案。如果它在 python 2.7 中工作,它必须在 python 3.3 中工作,反之亦然。因为我们确实有两台服务器。一个是使用python2.7,另一个是最新的。我们正在编写一个脚本,它应该在两个服务器上运行。很抱歉!
  • 这样的东西应该在标准库中。

标签: python python-2.7 python-3.x


【解决方案1】:

这是一个简单的递归版本:

def getpath(nested_dict, value, prepath=()):
    for k, v in nested_dict.items():
        path = prepath + (k,)
        if v == value: # found value
            return path
        elif hasattr(v, 'items'): # v is a dict
            p = getpath(v, value, path) # recursive call
            if p is not None:
                return p

例子:

print(getpath(dictionary, 'image/svg+xml'))
# -> ('dict1', 'part2', '.svg')

【讨论】:

    【解决方案2】:

    这是对嵌套 dicts 的迭代遍历,它还跟踪通向特定点的所有键。因此,一旦您在 dicts 中找到正确的值,您也已经拥有获取该值所需的键。

    如果您将以下代码放在 .py 文件中,它将按原样运行。 find_mime_type(...) 函数返回将您从原始字典中获取到所需值的键序列。 demo() 函数展示了如何使用它。

    d = {'dict1':
             {'part1':
                  {'.wbxml': 'application/vnd.wap.wbxml',
                   '.rl': 'application/resource-lists+xml'},
              'part2':
                  {'.wsdl': 'application/wsdl+xml',
                   '.rs': 'application/rls-services+xml',
                   '.xop': 'application/xop+xml',
                   '.svg': 'image/svg+xml'}},
         'dict2':
             {'part1':
                  {'.dotx': 'application/vnd.openxmlformats-..',
                   '.zaz': 'application/vnd.zzazz.deck+xml',
                   '.xer': 'application/patch-ops-error+xml'}}}
    
    
    def demo():
        mime_type = 'image/svg+xml'
        try:
            key_chain = find_mime_type(d, mime_type)
        except KeyError:
            print ('Could not find this mime type: {0}'.format(mime_type))
            exit()
        print ('Found {0} mime type here: {1}'.format(mime_type, key_chain))
        nested = d
        for key in key_chain:
            nested = nested[key]
        print ('Confirmation lookup: {0}'.format(nested))
    
    
    def find_mime_type(d, mime_type):
        reverse_linked_q = list()
        reverse_linked_q.append((list(), d))
        while reverse_linked_q:
            this_key_chain, this_v = reverse_linked_q.pop()
            # finish search if found the mime type
            if this_v == mime_type:
                return this_key_chain
            # not found. keep searching
            # queue dicts for checking / ignore anything that's not a dict
            try:
                items = this_v.items()
            except AttributeError:
                continue  # this was not a nested dict. ignore it
            for k, v in items:
                reverse_linked_q.append((this_key_chain + [k], v))
        # if we haven't returned by this point, we've exhausted all the contents
        raise KeyError
    
    
    if __name__ == '__main__':
        demo()
    

    输出:

    在此处找到 image/svg+xml mime 类型:['dict1', 'part2', '.svg']

    确认查找:image/svg+xml

    【讨论】:

    • @kobehjohn:我在等待你的进一步解释
    • @LaxmikantGurnalkar 你试过了吗?您可以将所有这些代码放在一个 .py 文件中并运行它。它应该为您提供与我列出的相同的输出。如果这是您正在寻找的内容,那么我将提供更多文档。如果没有,请告诉我有什么不同。
    • @kobehjohn:我忙于完成这项任务。稍后,将在一个小时后进行处理。一旦测试将接受答案。谢谢
    • @LaxmikantGurnalkar 听起来不错。希望对你有效。我更新了答案代码以显示如何在获取键链后查找值。
    【解决方案3】:

    这是一个适用于嵌套列表和字典的复杂数据结构的解决方案

    import pprint
    
    def search(d, search_pattern, prev_datapoint_path=''):
        output = []
        current_datapoint = d
        current_datapoint_path = prev_datapoint_path
        if type(current_datapoint) is dict:
            for dkey in current_datapoint:
                if search_pattern in str(dkey):
                    c = current_datapoint_path
                    c+="['"+dkey+"']"
                    output.append(c)
                c = current_datapoint_path
                c+="['"+dkey+"']"
                for i in search(current_datapoint[dkey], search_pattern, c):
                    output.append(i)
        elif type(current_datapoint) is list:
            for i in range(0, len(current_datapoint)):
                if search_pattern in str(i):
                    c = current_datapoint_path
                    c += "[" + str(i) + "]"
                    output.append(i)
                c = current_datapoint_path
                c+="["+ str(i) +"]"
                for i in search(current_datapoint[i], search_pattern, c):
                    output.append(i)
        elif search_pattern in str(current_datapoint):
            c = current_datapoint_path
            output.append(c)
        output = filter(None, output)
        return list(output)
    
    
    if __name__ == "__main__":
        d = {'dict1':
                 {'part1':
                      {'.wbxml': 'application/vnd.wap.wbxml',
                       '.rl': 'application/resource-lists+xml'},
                  'part2':
                      {'.wsdl': 'application/wsdl+xml',
                       '.rs': 'application/rls-services+xml',
                       '.xop': 'application/xop+xml',
                       '.svg': 'image/svg+xml'}},
             'dict2':
                 {'part1':
                      {'.dotx': 'application/vnd.openxmlformats-..',
                       '.zaz': 'application/vnd.zzazz.deck+xml',
                       '.xer': 'application/patch-ops-error+xml'}}}
    
        d2 = {
            "items":
                {
                    "item":
                        [
                            {
                                "id": "0001",
                                "type": "donut",
                                "name": "Cake",
                                "ppu": 0.55,
                                "batters":
                                    {
                                        "batter":
                                            [
                                                {"id": "1001", "type": "Regular"},
                                                {"id": "1002", "type": "Chocolate"},
                                                {"id": "1003", "type": "Blueberry"},
                                                {"id": "1004", "type": "Devil's Food"}
                                            ]
                                    },
                                "topping":
                                    [
                                        {"id": "5001", "type": "None"},
                                        {"id": "5002", "type": "Glazed"},
                                        {"id": "5005", "type": "Sugar"},
                                        {"id": "5007", "type": "Powdered Sugar"},
                                        {"id": "5006", "type": "Chocolate with Sprinkles"},
                                        {"id": "5003", "type": "Chocolate"},
                                        {"id": "5004", "type": "Maple"}
                                    ]
                            },
    
                            ...
    
                        ]
                }
        }
    
    pprint.pprint(search(d,'svg+xml','d'))
    >> ["d['dict1']['part2']['.svg']"]
    
    pprint.pprint(search(d2,'500','d2'))
    >> ["d2['items']['item'][0]['topping'][0]['id']",
     "d2['items']['item'][0]['topping'][1]['id']",
     "d2['items']['item'][0]['topping'][2]['id']",
     "d2['items']['item'][0]['topping'][3]['id']",
     "d2['items']['item'][0]['topping'][4]['id']",
     "d2['items']['item'][0]['topping'][5]['id']",
     "d2['items']['item'][0]['topping'][6]['id']"]
    

    【讨论】:

    • 您好,欢迎来到 SO!请添加更多详细信息并解释您的答案。
    • 我不是一个普通的程序员。我为新手编码风格道歉。该解决方案适用于任何具有嵌套字典和列表字典的复杂字典。逻辑很简单。我从顶层层次结构开始,递归地遍历字典并列出层次结构。在每一点,我检查是否有任何键或其值(对于 dicts)或任何索引或其值(对于列表)与搜索模式匹配。如果匹配,则将直到该点的路径推入输出列表。
    【解决方案4】:

    这是执行此类操作的两种类似的快速而肮脏的方法。函数 find_parent_dict1 使用列表推导,但如果您对此不满意,则 find_parent_dict2 使用臭名昭著的嵌套 for 循环。

    Dictionary = {'dict1':{'part1':{'.wbxml':'1','.rl':'2'},'part2':{'.wbdl':'3','.rs':'4'}},'dict2':{'part3':{'.wbxml':'5','.rl':'6'},'part4':{'.wbdl':'1','.rs':'10'}}}
    
    value = '3'
    
    def find_parent_dict1(Dictionary):
        for key1 in Dictionary.keys():
            item = {key1:key2 for key2 in Dictionary[key1].keys() if value in Dictionary[key1][key2].values()}
            if len(item)>0:
                return item
    
    find_parent_dict1(Dictionary)
    
    
    def find_parent_dict2(Dictionary):
        for key1 in Dictionary.keys():
            for key2 in Dictionary[key1].keys():
                if value in Dictionary[key1][key2].values():
                    print {key1:key2}
    
    find_parent_dict2(Dictionary)
    

    【讨论】:

    • 它不适用于任意嵌套字典。 .keys() 调用在 for-loops 中是多余的。
    【解决方案5】:

    遍历嵌套字典以查找特定值。成功后,将打印该值的完整密钥路径。我留下了所有的 cmets 和 print 语句用于教学目的(这不是生产代码!)

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    """
    Created on Mon Jan 24 17:16:46 2022
    
    @author: wellington
    """
    
    
    class Tree(dict):
        """
        allows autovivification as in Perl hashes
        """
    
        def __missing__(self, key):
            value = self[key] = type(self)()
            return value
    
    # tracking the key sequence when seeking the target
    key_list = Tree()
    
    # dict storing the target success result
    success = Tree()
    
    
    # example nested dict of dicts and lists
    E = {
        'AA':
            {
              'BB':
                   {'CC':
                         {
                          'DD':
                              {
                               'ZZ':'YY',
                               'WW':'PP'
                               },
                           'QQ':
                               {
                                'RR':'SS'
                                },
                         },
                    'II': 
                         {
                          'JJ':'KK'
                         }, 
                    'LL':['MM', 'GG', 'TT']
                   }
            }
        }
    
    
    def find_keys_from_value(data, target):
        """
        recursive function -
        given a value it returns all the keys in the path to that value within
        the dict "data"
        there are many paths and many false routes
        at the end of a given path if success has not been achieved
        the function discards keys to get back to the next possible path junction
        """
    
        print(f"the number of keys in the local dict is {len(data)}")
        key_counter = 0
    
        for key in data:
            key_counter += 1
    
            # if target has been located stop iterating through keys
            if success[target] == 1:
                break
            else:
                # eliminate prior key from path that did not lead to success
                if key_counter > 1:
                    k_list.pop()
                # add key to new path
                k_list.append(key)
                print(f"printing k_list after append{k_list}")
    
            # if target located set success[target] = 1 and exit
            if key == target or data[key] == target:
                key_list[target] = k_list
                success[target] = 1
                break
            # if the target has not been located check to see if the value
            # associated with the new key is a dict and if so return to the
            # recursive function with the new dict as "data"
            elif isinstance(data[key], dict):
                print(f"\nvalue is dict\n {data[key]}")
                find_keys_from_value(data[key], target)
    
            # check to see if the value associated with the new key is a list
            elif isinstance(data[key], list):
                # print("\nv is list\n")
                # search through the list
                for i in data[key]:
    
                    # check to see if the list element is a dict
                    # and if so return to the recursive function with
                    # the new dict as "data
                    if isinstance(i, dict):
                        find_keys_from_value(i, target)
    
                    # check to see if each list element is the target
                    elif i == target:
                        print(f"list entry {i} is target")
                        success[target] = 1
                        key_list[target] = k_list
                    elif i != target:
                        print(f"list entry {i} is not target")
                        print(f"printing k_list before pop_b {k_list}")
                        print(f"popping off key_b {key}")
    
            # so if value is not a key and not a list and not the target then
            # discard the key from the key list
            elif data[key] != target:
                print(f"value {data[key]} is not target")
                print(f"printing k_list before removing key_before {k_list}")
                print(f"removing key_c {key}")
                k_list.remove(key)
    
    
    # select target values
    values = ["PP", "SS", "KK", "TT"]
    success = {}
    
    for target in values:
        print(f"\nlooking for target {target}")
        success[target] = 0
        k_list = []
        find_keys_from_value(E, target)
        print(f"\nprinting key_list for target {target}")
        print(f"{key_list[target]}\n")
        print("\n****************\n\n")
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-02
      • 1970-01-01
      • 2021-11-21
      • 2020-10-01
      • 2019-06-18
      • 1970-01-01
      • 2023-02-10
      • 2015-10-15
      相关资源
      最近更新 更多