Reputation: 351
I am new to python and I've tried to search but can seem to find a sample of what I am trying to accomplish. Any ideas are much appreciated. I am working with a nested dictionary with lots of key and values but I only want to print specific ones using a filtered list variable.
my_nested_dict = {"global": {"peers": {"15.1.1.1": {"remote_id": "15.1.1.1", "address_family": {"ipv4": {"sent_prefixes": 1, "received_prefixes": 4, "accepted_prefixes": 4}}, "remote_as": 65002, "uptime": 13002, "is_enabled": true, "is_up": true, "description": "== R3 BGP Neighbor ==", "local_as": 65002}}, "router_id": "15.1.1.2"}}
I would like to a filter through it and choose which keys and values to print out
filtered_list = ['peers', 'remote_id', 'remote_as', 'uptime']
and achieve a out out of
peers: 15.1.1.1
remote_id: 15.1.1.1
remote_as: 65002
uptime: 13002
Upvotes: 13
Views: 12696
Reputation: 837
You could use jsonparse_ng (pip install jsonparse-ng). Without the if/ else the value the the key "peers" is printed like it is (a dict).
from jsonpath_ng.ext import parse
my_nested_dict = {
"global": {
"peers": {
"15.1.1.1": {
"address_family": {
"ipv4": {
"accepted_prefixes": 4,
"received_prefixes": 4,
"sent_prefixes": 1
}
},
"description": "== R3 BGP Neighbor ==",
"is_enabled": true,
"is_up": true,
"local_as": 65002,
"remote_as": 65002,
"remote_id": "15.1.1.1",
"uptime": 13002
}
},
"router_id": "15.1.1.2"
}
}
filtered_list = ['peers', 'remote_id', 'remote_as', 'uptime']
for list_item in filtered_list:
found = parse(f'$..{list_item}').find(my_nested_dict)
if found:
if list_item == 'peers':
print(f'{list_item} - {"".join(found[0].value.keys())}')
else:
print(f'{list_item} - {found[0].value}')
Upvotes: 0
Reputation: 61
In Python 3 it's more efficient and simpler to use generators instead of recursion:
from typing import Any, Tuple, Generator, FrozenSet
def search_in_dict(d: Any, keys: FrozenSet[str]) -> Generator[Tuple[str, Any], None, None]:
"""
Generate pairs key-value for found keys
"""
if not isinstance(d, dict):
return
for key, value in d.items():
if key in keys:
if isinstance(value, dict):
# Special case: return the first key from nested dict as value
yield key, tuple(value.keys())[0]
else:
yield key, value
# continue to search deeper
yield from search_in_dict(value, keys)
and then fold it to dict again:
flatten_dict_with_results = dict(kv for kv in search_in_dict(my_nested_dict, keys=frozenset(filtered_list)))
Upvotes: 0
Reputation: 71471
@JacobIRR has posted a great answer, but since you are attempting to join the matching keys with the its corresponding value, a much shorter solution arises:
my_nested_dict = {"global": {"peers": {"15.1.1.1": {"remote_id": "15.1.1.1", "address_family": {"ipv4": {"sent_prefixes": 1, "received_prefixes": 4, "accepted_prefixes": 4}}, "remote_as": 65002, "uptime": 13002, "is_enabled": True, "is_up": True, "description": "== R3 BGP Neighbor ==", "local_as": 65002}}, "router_id": "15.1.1.2"}}
_list = ['peers', 'remote_id', 'remote_as', 'uptime']
def _join(a, b):
return '{}:{}\n'.format(a, _keys(b, True) if isinstance(b, dict) else b)
def _keys(_d, flag = False):
return ''.join(_join(a, b) if a in _list else (a+'\n' if flag else '')+_keys(b)
for a, b in _d.items())
print(get_keys(my_nested_dict))
Output:
peers:15.1.1.1
remote_id:15.1.1.1
remote_as:65002
uptime:13002
Upvotes: 0
Reputation: 41
On top of @JacobIRR;s answer, I would say you can try caching the recursed data in a flat dict. That way, it will be much faster than recurse every time. You need not worry about memory since the values in the flat dict will simply refer to the original objects in the deep dictionary. I will leave the modification of JacobIRR's code to you :).
Upvotes: 0
Reputation: 8966
Use recursion and isinstance
:
my_nested_dict = {"global": {"peers": {"15.1.1.1": {"remote_id": "15.1.1.1", "address_family": {"ipv4": {"sent_prefixes": 1, "received_prefixes": 4, "accepted_prefixes": 4}}, "remote_as": 65002, "uptime": 13002, "is_enabled": True, "is_up": True, "description": "== R3 BGP Neighbor ==", "local_as": 65002}}, "router_id": "15.1.1.2"}}
filtered_list = ['peers', 'remote_id', 'remote_as', 'uptime']
def seek_keys(d, key_list):
for k, v in d.items():
if k in key_list:
if isinstance(v, dict):
print(k + ": " + list(v.keys())[0])
else:
print(k + ": " + str(v))
if isinstance(v, dict):
seek_keys(v, key_list)
seek_keys(my_nested_dict, filtered_list)
Note: There is a built in assumption here that if you ever want the "value" from a key whose value is another dictionary, you get the first key.
Upvotes: 15