How to change all the values in a nested dictionary in python?

Question:

I have sample data that looks like this (nested and is dynamic so it does change on the fly):

  ...   
  "counts":{
     "1":{
        "1":21082,
        "2":14999
     },
     "2":{
        "1":9180,
        "2":10023
     }
  },
  ...

I need to recursively go through and replace all the values with -1, so the result will be something like:

  ...   
  "counts":{
     "1":{
        "1":-1,
        "2":-1
     },
     "2":{
        "1":-1,
        "2":-1
     }
  },
  ...

I can do it if the dictionary is only one level deep like so:

result['counts'] = {x: '-1' for x in result['counts']}

How do I do it for 2 or more levels on the fly?

Also sometimes the key’s can be like 1(1, 2)` or other things like so:

  ...   
  "counts":{
     "(1, 2)":{
        "1":21082,
        "2":14999
     },
     "(2, 1)":{
        "1":9180,
        "2":10023
     }
  },
  ...

Is there any easy way to do this?

Asked By: cdub

||

Answers:

Use a recursive function that calls itself on each nested dict:

>>> def replace_value(d, new_val):
...     return {
...         k: replace_value(v, new_val) if isinstance(v, dict) else new_val
...         for k, v in d.items()
...     }
...
>>> replace_value({"foo": {"foo": "bar"}}, -1)
{'foo': {'foo': -1}}

Alternate version with the base case outside the comprehension:

>>> def replace_value(d, new_val):
...     if not isinstance(d, dict):
...         return new_val
...     return {k: replace_value(v, new_val) for k, v in d.items()}
...
>>> replace_value({"foo": {"foo": "bar"}}, -1)
{'foo': {'foo': -1}}
Answered By: Samwise

My choice for similar tasks is remap from boltons

>>> def visitor(path, key, value):
...     if not isinstance(value, dict):
...         return key, -1
...     return True
... 
>>> from boltons.iterutils import remap  # pip install boltons
>>> d
{'counts': {'1': {'1': 21082, '2': 14999}, '2': {'1': 9180, '2': 10023}}}
>>> remap(d, visit=visitor)
{'counts': {'1': {'1': -1, '2': -1}, '2': {'1': -1, '2': -1}}}

The visit callable accepts a path, key, and value, and should return the new key and value (or return True as a shorthand to keep old item unmodified).

I prefer this to a recursive function, because it uses a stack-based iterative approach. It is too easy to blow past the recursion limit in Python when dealing with nested data structures.

Answered By: wim