Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python filter nested dict given list of key names

Is there a way to filter a nested dict in Python, so I can see only the keys I'd specified ? Example:

x = {
  "field": [
    {
      "nm_field": "ch_origem_sistema_chave",
      "inf_tabelado": {
        "dropdown_value": "",
        "dropdown_key": "",
        "url_lista": "",
        "chave_relacional": ""
      },
    },
    {
      "nm_field": "ax_andamento_data",
      "inf_tabelado": {
        "dropdown_value": "",
        "dropdown_key": "",
        "url_lista": "",
        "chave_relacional": ""
      },
    }
  ],
  "_metadata": {
    "dt_reg": "22/01/2014 16:17:16",
    "dt_last_up": "10/04/2014 16:30:44",
  },
  "url_detalhes": "/DetalhesDocsPro.aspx",
  "url_app": "/docspro",
}

y = filter(x, ['dropdown_value', 'nm_field', 'url_app', 'dt_reg'])

Then var y would be something like:

{
  "field": [
    {
      "nm_field": "ch_origem_sistema_chave",
      "inf_tabelado": {
        "dropdown_value": "",
      },
    },
    {
      "nm_field": "ax_andamento_data",
      "inf_tabelado": {
        "dropdown_value": "",
      },
    }
  ],
  "_metadata": {
    "dt_reg": "22/01/2014 16:17:16",
  },
  "url_app": "/docspro",
}

I've tried to do something using defaultdict, but had no success with lists at any level of recursion. Also I found dificulty while working with different data structures.

like image 219
user1538560 Avatar asked Jan 24 '26 02:01

user1538560


2 Answers

Here's a modified version of 2rs2ts's answer that returns a new object rather than modifying the old one (and handles filtering on non-leaf nodes):

import copy

def fltr(node, vals):
    if isinstance(node, dict):
        retVal = {}
        for key in node:
            if key in vals:
                retVal[key] = copy.deepcopy(node[key])
            elif isinstance(node[key], list) or isinstance(node[key], dict):
                child = fltr(node[key], vals)
                if child:
                    retVal[key] = child
        if retVal:
             return retVal
        else:
             return None
    elif isinstance(node, list):
        retVal = []
        for entry in node:
            child = fltr(entry, vals)
            if child:
                retVal.append(child)
        if retVal:
            return retVal
        else:
            return None

With this, you will call

y = fltr(x, ['dropdown_value', 'nm_field', 'url_app', 'dt_reg'])

and get

{
    "field": [
        {
            "inf_tabelado": {
                "dropdown_value": ""
            },
            "nm_field": "ch_origem_sistema_chave"
        },
        {
            "inf_tabelado": {
                "dropdown_value": ""
            },
            "nm_field": "ax_andamento_data"
        }
    ],
    "url_app": "/docspro",
    "_metadata": {
        "dt_reg": "22/01/2014 16:17:16"
    }
}

Note that this will return None if everything is filtered. For example,

fltr(x, [])

will always return None, no matter what is in x.

like image 107
Rob Watts Avatar answered Jan 25 '26 14:01

Rob Watts


Here's a solution which walks the structure in a depth-first manner to find the "leaf" nodes which you are checking to see if they're in your list of elements to preserve. When it finds such an element, it removes it from the dictionary with del. (So this is done in-place.)

def fltr(d, vals):
    if isinstance(d, dict):
        vals_to_del = []
        for k in d:
            if k in vals:
                continue
            if not isinstance(d[k], list) and not isinstance(d[k], dict):
                if k not in vals:
                    vals_to_del.append(k)
            else:
                fltr(d[k], vals)
        for k in vals_to_del:
            del d[k]
    elif isinstance(d, list):
        for i in d:
            fltr(i, vals)

Note that I didn't define a function called filter, because it's a built-in one and you don't want to shadow it.

>>> fltr(x, ['dropdown_value', 'nm_field', 'url_app', 'dt_reg'])
>>> x
{'field': [{'inf_tabelado': {'dropdown_value': ''}, 'nm_field': 'ch_origem_sistema_chave'}, {'inf_tabelado': {'dropdown_value': ''}, 'nm_field': 'ax_andamento_data'}], 'url_app': '/docspro', '_metadata': {'dt_reg': '22/01/2014 16:17:16'}}
like image 33
2rs2ts Avatar answered Jan 25 '26 14:01

2rs2ts



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!