11

I want to make a function that iterate through a nested dict that include list. For each value that match with a keyword, the function replace it with another keyword.

It's not important if the function return another dict or if it change the main dict.

I tried to separate the case: - if the data is a dict, make something - if the data is a list, make something else

DICT:


data_dict = {
  "name": "AAAAAA",
  "content": "BBBBBB",
  "dat": [
    {
      "author": {
        "name": "CCCCCC",
        "surname": "DDDDDD",
      },
      "title": "FFFFFF",
      "color": 15258703,
      "fields": [
        {
          "name": "GGGGGG",
          "value": "HHHHHH",
        },
        {
          "name": "IIIIII",
          "value": "JJJJJJ",
        }

      ],
      "thumbnail": {
        "url": "KKKKKK"
      },
      "image": {
        "url": "LLLLLL"
      },
      "footer": {
        "text": "MMMMMMM",
        "icon_url": "NNNNNN"
      }
    }
  ]
}


Now I'm just trying to change each value to see if I'm iterate as well. I can print all the data_dict with value changed, but I can't manage it in a dict...


def recursive_replace_valuer(data, match, repl):

    if isinstance(data, list):
        for l in data:
            recursive_replace_valuer(l, match, repl)
            if isinstance(l, dict):
                recursive_replace_valuer(l, match, repl)




    elif isinstance(data, dict):
        for k,v in data.items():
            recursive_replace_valuer(v, match, repl)
            data[k] = '______'
        print(data)

recursive_replace_valuer(data_dict, 'a','a')
blhsing
  • 91,368
  • 6
  • 71
  • 106
M. T.
  • 396
  • 6
  • 24

1 Answers1

20

The function should recursively apply the function to the values in a dict or a list until the input data is neither a dict nor a list, at which point return the replacement value repl if the given data match the given search string match:

def replace(data, match, repl):
    if isinstance(data, dict):
        return {k: replace(v, match, repl) for k, v in data.items()}
    elif isinstance(data, list):
        return [replace(i, match, repl) for i in data]
    else:
        return repl if data == match else data

Or, to replace the original data in-place, make the function iterate through the given dict or list and replace a value by the key (for dict) or index (for list) if it's a match, and recursively apply the function to the value:

def replace(data, match, repl):
    if isinstance(data, (dict, list)):
        for k, v in (data.items() if isinstance(data, dict) else enumerate(data)):
            if v == match:
                data[k] = repl
            replace(v, match, repl)
jtlz2
  • 7,700
  • 9
  • 64
  • 114
blhsing
  • 91,368
  • 6
  • 71
  • 106
  • What if the value is a `set`? – Basil Musa Dec 17 '20 at 13:52
  • @BasilMusa Just add `elif isinstance(data, set): return {replace(i, match, repl) for i in data}` – blhsing Dec 17 '20 at 20:22
  • dictionary keys changed during iteration – Amulya Acharya Jun 12 '21 at 03:13
  • @AmulyaAcharya Are you talking about the error "RuntimeError: dictionary changed size during iteration"? Can you provide an example input dataset that can cause this error? All that `data[k] = repl` is doing is to assign a new value to an existing key, so I don't see how it can possibly change the size of the dict to produce that error. – blhsing Jun 12 '21 at 04:00