Python:替换嵌套字典中的值

4

每当键为'current_values'时,我希望用相同值的整数替换(格式化为字符串的)值。

d = {'id': '10', 'datastreams': [{'current_value': '5'}, {'current_value': '4'}]}

期望输出:

d = {'id': '10', 'datastreams': [{'current_value': 5}, {'current_value': 4}]}

5
你到目前为止尝试了什么? - DirtyBit
你混合使用字典和列表可能会使问题复杂化。 - ggrelet
2
@DirtyBit 他可能是 Stack Overflow 的新手,所以用负面反应吓唬他并不是很友好。 - andreihondrari
@JohnnyMcFly通常最好遵循MCVE指南中定义的原则,通过提供您尝试解决问题的示例来解决问题。请记住这一点,以备将来之需。 - andreihondrari
1
抱歉各位,你们是对的,我将来会考虑到这点的,谢谢! - JonnyMcFly
@andreihondrari,实际上,我并不是有意冒犯或者表达负面情绪。 - DirtyBit
8个回答

21
下面的代码段替换了(字典中的)值的子字符串。它适用于嵌套的JSON结构,并处理JSON、列表和字符串类型。如果需要,您可以轻松添加其他类型。
def dict_replace_value(d: dict, old: str, new: str) -> dict:
    x = {}
    for k, v in d.items():
        if isinstance(v, dict):
            v = dict_replace_value(v, old, new)
        elif isinstance(v, list):
            v = list_replace_value(v, old, new)
        elif isinstance(v, str):
            v = v.replace(old, new)
        x[k] = v
    return x


def list_replace_value(l: list, old: str, new: str) -> list:
    x = []
    for e in l:
        if isinstance(e, list):
            e = list_replace_value(e, old, new)
        elif isinstance(e, dict):
            e = dict_replace_value(e, old, new)
        elif isinstance(e, str):
            e = e.replace(old, new)
        x.append(e)
    return x

# See input and output below
output = dict_replace_value(input, 'string', 'something')

输入:

input = {
    'key1': 'a string',
    'key2': 'another string',
    'key3': [
        'a string',
        'another string',
        [1, 2, 3],
        {
            'key1': 'a string',
            'key2': 'another string'
        }
    ],
    'key4': {
        'key1': 'a string',
        'key2': 'another string',
        'key3': [
            'a string',
            'another string',
            500,
            1000
        ]
    },
    'key5': {
        'key1': [
            {
                'key1': 'a string'
            }
        ]
    }
}

输出:

print(output)

{
   "key1":"a something",
   "key2":"another something",
   "key3":[
      "a something",
      "another something",
      [
         1,
         2,
         3
      ],
      {
         "key1":"a something",
         "key2":"another something"
      }
   ],
   "key4":{
      "key1":"a something",
      "key2":"another something",
      "key3":[
         "a something",
         "another something",
         500,
         1000
      ]
   },
   "key5":{
      "key1":[
         {
            "key1":"a something"
         }
      ]
   }
}

1
这是最通用和可重复使用的答案,并且应该标记为正确答案!!! - Yordan Georgiev
2
我也遇到了同样的问题,大多数答案都只涉及一级嵌套结构。所以我才想出了上面的答案。 - Cloudkollektiv
1
@Nebulastic 很棒的回答。没有人比你更深入了解。 - Martin
很棒的答案,让我想到了一个改进的方法,可以运行函数而不是替换。请见下文。谢谢。 - undefined

4
d = {'id': '10', 'datastreams': [{'current_value': '5'}, {'current_value': '4'}]}

for elem in d['datastreams']:      # for each elem in the list datastreams
    for k,v in elem.items():       # for key,val in the elem of the list 
        if 'current_value' in k:   # if current_value is in the key
            elem[k] = int(v)       # Cast it to int
print(d)

输出:

{'id': '10', 'datastreams': [{'current_value': 5}, {'current_value': 4}]}

2
一种通用的方法(假设您事先不知道字典的哪个键指向列表)是迭代字典并检查其值的类型,然后根据需要再次迭代每个值。
在您的情况下,您的字典可能包含字典列表作为值,因此只需检查值是否为列表类型,如果是,则迭代列表并更改所需的字典即可。
可以使用以下函数递归地完成:

最初的回答:

def f(d):
    for k,v in d.items():
        if k == 'current_value':
            d[k] = int(v)
        elif type(v) is list:
            for item in v:
                if type(item) is dict:
                    f(item)

>>> d = {'id': '10', 'datastreams': [{'current_value': '5'}, {'current_value': '4'}]}
>>> f(d)
>>> d
{'id': '10', 'datastreams': [{'current_value': 5}, {'current_value': 4}]}  

这是最简单的答案。 - ggrelet

1
可以使用列表推导式完成:
d['datastreams'] = [{'current_value': int(ds['current_value'])} if ('current_value' in ds) else ds for ds in d['datastreams']]

0
你可以使用 ast.literal_eval 函数来评估 d['datastreams'] 列表中带有 current_value 键的 items 的基础值。然后,使用 isinstance 函数检查该类型是否为 int。最后,将这些值强制转换为 int 类型。
import ast
d = {'id': '10', 'datastreams': [{'current_value': '5'}, {'current_value': '4'}]}
for i in d['datastreams']:
    for k,v in i.items():
        if 'current_value' in k and isinstance(ast.literal_eval(v),int):
            i[k] = int(v)
#Output:
print(d)
{'id': '10', 'datastreams': [{'current_value': 5}, {'current_value': 4}]}

0
你可以使用这个方法,它会循环检查列表中的 current_value 并通过 int() 函数将其更改为整数值:
for value in d.values():
    for element in value:
        if 'current_value' in element:
            element['current_value'] = int(element['current_value'])

0

将alec_djinn的解决方案稍微扩展一下,以处理嵌套字典:

def f(d):
    for k,v in d.items():
        if k == 'current_value':
            d[k] = int(v)
        elif type(v) is list:
            for item in v:
                if type(item) is dict:
                    f(item)
        if type(v) is dict:
            f(v)

它应该已经处理嵌套对象。 - Giovanni Patruno
1
它只处理了嵌套列表对象,但没有处理嵌套字典对象。 - Idan Regev
我尝试运行你的代码,但是它无法工作,因为“key”未定义。 - Giovanni Patruno
1
谢谢提醒,我已经修复了代码。 - Idan Regev

0
基于Cloudkollektiv的优秀想法,我对代码进行了重构,基本上可以实现与原始函数相同的功能,但是集成在一个函数中。 我还增加了一种可能性,即不仅可以用一个新的字符串替换旧的字符串,还可以用fn(old)来替换old,其中fn是作为参数传递给old的函数。
def replace_in_iterable(src: Union[dict, list], old: Union[str, Callable], new: str = None):
    """
    Replaces every instance of old with new in a list/dict
    If old is a callable function, it will replace every instance of old win callable(old)
    """
    def _replace_in_iterable(_src):
        if isinstance(_src, dict) or isinstance(_src, list):
            _src = replace_in_iterable(_src, old, new)
        elif isinstance(old, Callable):
            _src = old(_src)
        elif isinstance(_src, str):
            _src = _src.replace(old, new)
        return _src

    if isinstance(src, dict):
        result = {}
        for key, value in src.items():
            result[key] = _replace_in_iterable(value)
    elif isinstance(src, list):
        result = []
        for entry in src:
            result.append(_replace_in_iterable(entry))
    else:
        result = _replace_in_iterable(src)
    return result

TL;DR:你可以直接通过pip安装此功能作为软件包使用。
pip install ofunctions.misc

然后与之一起使用
from ofunctions.misc import replace_in_iterable

def test(string):
   return f"-{string}-"

output = replace_in_iterable(input, test)

输入

input = {
    'key1': 'a string',
    'key2': 'another string',
    'key3': [
        'a string',
        'another string',
        [1, 2, 3],
        {
            'key1': 'a string',
            'key2': 'another string'
        }
    ],
    'key4': {
        'key1': 'a string',
        'key2': 'another string',
        'key3': [
            'a string',
            'another string',
            500,
            1000
        ]
    },
    'key5': {
        'key1': [
            {
                'key1': 'a string'
            }
        ]
    }
}

输出

input = {
    'key1': '-a string-',
    'key2': '-another string-',
    'key3': [
        '-a string-',
        '-another string-',
        ['-1-', '-2-', '-3-'],
        {
            'key1': '-a string-',
            'key2': '-another string-'
        }
    ],
    'key4': {
        'key1': '-a string-',
        'key2': '-another string-',
        'key3': [
            '-a string-',
            '-another string-',
            '-500-',
            '-1000-'
        ]
    },
    'key5': {
        'key1': [
            {
                'key1': '-a string-'
            }
        ]
    }
}

当然,原始的语法通过output = replace_in_iterable(input, "string", "something)仍然有效。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接