繁体   English   中英

如何更新嵌套字典中的值?

[英]How to update values in a nested dictionary?

我有两本词典:

data = {
  "filter":
    {
      "and":
        [
          {
            "or":
              [
                {
                  "and":
                    [
                      {"category": "profile", "key": "languages", "operator": "IN", "value": "EN"},
                      {"category": "skill", "key": "26366", "value": 100, "operator": "EQ"},
                    ],
                },
              ],
          },
          {"or": [{"category": "skill", "key": "45165", "operator": "NE"}]},
          {"or": [{"category": "skill", "key": "48834", "value": 80, "operator": "GT"}]},
          {"or": [{"category": "profile", "key": "gender", "operator": "EQ", "value": "FEMALE"}]},
        ],
    },
}

new_val = {'26366': '11616', '45165': '11613', '48834': '11618'}

我想用“new_val”字典中的值更新“data”字典中的值。

这样 26366(在“data”字典中)变为 11616(来自“new_val”字典),45165 变为 11613,48834 变为 11618。“data”字典嵌套可以不同(上下)

“data”字典中的key可以不同,不仅是“key”,还可以是“skill_id”、“filter_id”等。

并得到这个结果:

{
  "filter":
    {
      "and":
        [
          {
            "or":
              [
                {
                  "and":
                    [
                      {"category": "profile", "key": "languages", "operator": "IN", "value": "EN"},
                      {"category": "skill", "key": "11616", "value": 100, "operator": "EQ"},
                    ],
                },
              ],
          },
          {"or": [{"category": "skill", "key": "11613", "operator": "NE"}]},
          {"or": [{"category": "skill", "key": "11618", "value": 80, "operator": "GT"}]},
          {"or": [{"category": "profile", "key": "gender", "operator": "EQ", "value": "FEMALE"}]},
        ],
    },
}

要返回更新的字典而不修改旧字典:

def updated_in_depth(d, replace):
    if isinstance(d, dict):
        return {k: updated_in_depth(v, replace)
                for k,v in d.items()}
    elif isinstance(d, list):
        return [updated_in_depth(x, replace) for x in d]
    else:
        return replace.get(d, d)

使用您的datanew_val测试:

>>> updated_in_depth(data, new_val)
{'filter': {'and': [{'or': [{'and': [
                            {'category': 'profile', 'key': 'languages', 'operator': 'IN', 'value': 'EN'},
                            {'category': 'skill', 'key': '11616', 'value': 100, 'operator': 'EQ'}]}]},
                    {'or': [{'category': 'skill', 'key': '11613', 'operator': 'NE'}]},
                    {'or': [{'category': 'skill', 'key': '11618', 'value': 80, 'operator': 'GT'}]},
                    {'or': [{'category': 'profile', 'key': 'gender', 'operator': 'EQ', 'value': 'FEMALE'}]}]}}

使用这样的东西:

data['filter']['and']['or']['and'][1]['key']='11616'

要递归搜索密钥,您可以执行以下操作:

from copy import deepcopy


def replace(d, new_vals):
    if isinstance(d, dict):
        # replace key (if there's match):
        if "key" in d:
            d["key"] = new_vals.get(d["key"], d["key"])
        for v in d.values():
            replace(v, new_vals)
    elif isinstance(d, list):
        for v in d:
            replace(v, new_vals)


new_data = deepcopy(data)
replace(new_data, new_val)
print(new_data)

印刷:

{
    "filter": {
        "and": [
            {
                "or": [
                    {
                        "and": [
                            {
                                "category": "profile",
                                "key": "languages",
                                "operator": "IN",
                                "value": "EN",
                            },
                            {
                                "category": "skill",
                                "key": "11616",
                                "value": 100,
                                "operator": "EQ",
                            },
                        ]
                    }
                ]
            },
            {"or": [{"category": "skill", "key": "11613", "operator": "NE"}]},
            {
                "or": [
                    {
                        "category": "skill",
                        "key": "11618",
                        "value": 80,
                        "operator": "GT",
                    }
                ]
            },
            {
                "or": [
                    {
                        "category": "profile",
                        "key": "gender",
                        "operator": "EQ",
                        "value": "FEMALE",
                    }
                ]
            },
        ]
    }
}

如果您不需要数据副本,则可以省略deepcopy

replace(data, new_val)
print(data)

您可以像这样构建递归 function

def walk_dict(d):
    if isinstance(d, list):
        for item in d:
            walk_dict(item)
    elif isinstance(d, dict):
        if 'key' in d and d['key'] in new_val:
            d['key'] = new_val[d['key']]
        for k, v in d.items():
            walk_dict(v)


walk_dict(data)
print(data)

正如许多人所建议的那样,递归 function 可以解决问题:

def a(d):
    if isinstance(d, dict): # if dictionary, apply a to all values
        d = {k: a(d[k]) for k in d.keys()}
        return d
    elif isinstance(d, list): # if list, apply to all elements
        return [a(x) for x in d]
    else: # apply to d directly (it is a number, a string or a bool)
        return new_val[d] if d in new_val else d

a被调用时,它会检查变量d的类型:

  • 如果d是一个list ,它将a应用于列表的每个元素并返回更新后的列表
  • 如果ddict ,它将a应用于所有值并返回更新后的 dict
  • 否则,如果在new_val键中找到旧值,则返回映射的新值
data = {
  "filter":
    {
      "and":
        [
          {
            "or":
              [
                {
                  "and":
                    [
                      {"category": "profile", "key": "languages", "operator": "IN", "value": "EN"},
                       {"category": "skill", "key": "11616", "value": 100, "operator": "EQ"},
                    ],
                },
              ],
          },
          {"or": [{"category": "skill", "key": "11613", "operator": "NE"}]},
          {"or": [{"category": "skill", "key": "11618", "value": 80, "operator": "GT"}]},
          {"or": [{"category": "profile", "key": "gender", "operator": "EQ", "value": "FEMALE"}]},
        ],
        },
}
class Replace:
    def __init__(self,data):
        self.data=data
    def start(self,d):
        data = self.data
    
        
        def replace(data):
            if type(data) == list:
                for v in data:
                    replace(v)
            if type(data) == dict:
                for k,v in data.items():
                    if type(v) == dict:
                        replace(v)
                    if type(v) == str:
                        if v in d:
                            data[k] = d[v]
    
        replace(data)
        return data
new_data = Replace(data).start({'26366': '11616',
                                '45165': '11613',
                                '48834': '11618'})
print(new_data)


    
            
    


    

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM