Access Nested Dictionary Items Via a List of Keys

Access nested dictionary items via a list of keys?

Use reduce() to traverse the dictionary:

from functools import reduce  # forward compatibility for Python 3
import operator

def getFromDict(dataDict, mapList):
return reduce(operator.getitem, mapList, dataDict)

and reuse getFromDict to find the location to store the value for setInDict():

def setInDict(dataDict, mapList, value):
getFromDict(dataDict, mapList[:-1])[mapList[-1]] = value

All but the last element in mapList is needed to find the 'parent' dictionary to add the value to, then use the last element to set the value to the right key.

Demo:

>>> getFromDict(dataDict, ["a", "r"])
1
>>> getFromDict(dataDict, ["b", "v", "y"])
2
>>> setInDict(dataDict, ["b", "v", "w"], 4)
>>> import pprint
>>> pprint.pprint(dataDict)
{'a': {'r': 1, 's': 2, 't': 3},
'b': {'u': 1, 'v': {'w': 4, 'x': 1, 'y': 2, 'z': 3}, 'w': 3}}

Note that the Python PEP8 style guide prescribes snake_case names for functions. The above works equally well for lists or a mix of dictionaries and lists, so the names should really be get_by_path() and set_by_path():

from functools import reduce  # forward compatibility for Python 3
import operator

def get_by_path(root, items):
"""Access a nested object in root by item sequence."""
return reduce(operator.getitem, items, root)

def set_by_path(root, items, value):
"""Set a value in a nested object in root by item sequence."""
get_by_path(root, items[:-1])[items[-1]] = value

And for completion's sake, a function to delete a key:

def del_by_path(root, items):
"""Delete a key-value in a nested object in root by item sequence."""
del get_by_path(root, items[:-1])[items[-1]]

accessing nested dict from list of keys

One approach is the following:

def resolve_value(obj, keys):
for key in keys:
obj = obj[key]
return obj


k = ['l0', 'l1', 'l2']
d = {'l0': {'l1': {'l2': 'hello'}}}

print(resolve_value(d, k))

Output

hello

Can I access a nested dict with a list of keys?

This solution creates another dictionary with same keys and then updates the existing dictionary:

#!/usr/bin/env python

from six.moves import reduce


def update2(input_dictionary, new_value, loc):
"""
Update a dictionary by defining the keys.

Parameters
----------
input_dictionary : dict
new_value : object
loc : iterable
Location

Returns
-------
new_dict : dict

Examples
--------
>>> example = {'a': {'b': 'c'}, '1': {'2': {'3': {'4': '5'}}}}

>>> update2(example, 'new', ('a', 'b'))
{'a': {'b': 'new'}, '1': {'2': {'3': {'4': '5'}}}}

>>> update2(example, 'foo', ('1', '2', '3', '4'))
{'a': {'b': 'new'}, '1': {'2': {'3': {'4': 'foo'}}}}

>>> update2(example, 'bar', ('1', '2'))
{'a': {'b': 'new'}, '1': {'2': 'bar'}}
"""
new_dict = reduce(lambda x, y: {y: x}, reversed(loc), new_value)
input_dictionary.update(new_dict)
return input_dictionary

if __name__ == '__main__':
import doctest
doctest.testmod()

use string, list or tuple for access keys

Safe method to get value of nested dictionary

You could use get twice:

example_dict.get('key1', {}).get('key2')

This will return None if either key1 or key2 does not exist.

Note that this could still raise an AttributeError if example_dict['key1'] exists but is not a dict (or a dict-like object with a get method). The try..except code you posted would raise a TypeError instead if example_dict['key1'] is unsubscriptable.

Another difference is that the try...except short-circuits immediately after the first missing key. The chain of get calls does not.


If you wish to preserve the syntax, example_dict['key1']['key2'] but do not want it to ever raise KeyErrors, then you could use the Hasher recipe:

class Hasher(dict):
# https://stackoverflow.com/a/3405143/190597
def __missing__(self, key):
value = self[key] = type(self)()
return value

example_dict = Hasher()
print(example_dict['key1'])
# {}
print(example_dict['key1']['key2'])
# {}
print(type(example_dict['key1']['key2']))
# <class '__main__.Hasher'>

Note that this returns an empty Hasher when a key is missing.

Since Hasher is a subclass of dict you can use a Hasher in much the same way you could use a dict. All the same methods and syntax is available, Hashers just treat missing keys differently.

You can convert a regular dict into a Hasher like this:

hasher = Hasher(example_dict)

and convert a Hasher to a regular dict just as easily:

regular_dict = dict(hasher)

Another alternative is to hide the ugliness in a helper function:

def safeget(dct, *keys):
for key in keys:
try:
dct = dct[key]
except KeyError:
return None
return dct

So the rest of your code can stay relatively readable:

safeget(example_dict, 'key1', 'key2')

How to access a specific key in a nested dictionary?

You can do it like so:

itemdetails = {101: { 'Item Name':'Tshirt', 'Price':150.00, 'Stock':20},
102: { 'Item Name':'Shirt', 'Price':300.00, 'Stock':20},
103: { 'Item Name':'Shorts', 'Price':450.00, 'Stock':20},
104: { 'Item Name':'Trousers', 'Price':750.00, 'Stock':20}}


option = str(input("Press [C] for Item Code , [N] for Item Name: "))
if option=='n':
itemname=(input("Enter Item name: "))
print(list(filter(lambda item: item['Item Name'] == itemname, itemdetails))[0])
else:
itemcode=int(input("Enter Item code: "))

print(itemdetails[itemcode])

How to access nested keys and their values to modify or delete all at once?

By nested keys and values, I assume you mean the ones under variants. You can access those in much the same way as you iterated through items:

variant_list = []
for variant in item[variants]:
url = variant['url']
# and so on... for whatever other keys you're interested in
new_variant = {'url':url} # and whatever other keys you want
variant_list.append(new_variant)

I have to wonder, though, why you're reconstructing dictionaries that are similar to ones that JSON gave you? For many purposes, you might as well stick with the dictionary that JSON gave you.

Access nested dictionary without loops

You can use functools.reduce.

>>> from functools import reduce
>>> mydict = {'a1':{'b1':1}, 'a2':2}
>>> keys = ['a1', 'b1']
>>> reduce(dict.get, keys, mydict)
1

dict.get is a function that takes two arguments, the dict and a key (and another optional argument not relevant here). mydict is used as the initial value.

In case you ever need the intermediary results, use itertools.accumulate.

>>> from itertools import accumulate
>>> list(accumulate([mydict] + keys, dict.get))
[{'a1': {'b1': 1}, 'a2': 2}, {'b1': 1}, 1]

Unfortunately, the function does not take an optional initializer argument, so we are prepending mydict to keys.



Related Topics



Leave a reply



Submit