Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I would like to access a dictionary programmatically. I know how to do this with a recursive function, but is there a simpler way?

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

keys = ('a', 'b')
example[keys] = 'new'
# Now it should be
#     example = {'a': {'b': 'new'},
#                '1': {'2': {'3': {'4': '5'}}}}


keys = ('1', '2', '3', '4')
example[keys] = 'foo'
# Now it should be
#     example = {'a': {'b': 'new'},
#                '1': {'2': {'3': {'4': 'foo'}}}}


keys = ('1', '2')
example[keys] = 'bar'
# Now it should be
#     example = {'a': {'b': 'new'},
#                '1': {'2': 'bar'}}
like image 473
Martin Thoma Avatar asked Feb 16 '18 13:02

Martin Thoma


People also ask

How to access nested Dictionary items via a list of keys with Python?

{'a': {'r': 100, 's': 2, 't': 3}, 'b': {'u': 1, 'v': {'x': 1, 'y': 2, 'z': 3}, 'w': 3}} To access nested dictionary items via a list of keys with Python, we can use the reduce function with the operator.getitem method to get the dictionary item with the array of keys forming the path to the dictionary item.

What is nested_Dict in Python?

Here, the nested_dict is a nested dictionary with the dictionary dictA and dictB. They are two dictionary each having own key and value. We're going to create dictionary of people within a dictionary. When we run above program, it will output:

How to slicing nested Dictionary?

Slicing Nested Dictionary is not possible. We can shrink or grow nested dictionary as need. Like Dictionary, it also has key and value. Dictionary are accessed using key.

How to iterate through a nested dictionary using for loops?

Using the for loops, we can iterate through each elements in a nested dictionary. Example 7: How to iterate through a Nested dictionary? When we run above program, it will output: In the above program, the first loop returns all the keys in the nested dictionary people. It consist of the IDs p_id of each person.


2 Answers

What you seem to want to do is define your own class of dictionary that supports this kind of indexing. We can attain a fairly neat syntax by using the fact that when you do d[1, 2, 3], Python actually passes the tuple (1, 2, 3) to __getitem__.

class NestedDict:
    def __init__(self, *args, **kwargs):
        self.dict = dict(*args, **kwargs)

    def __getitem__(self, keys):
        # Allows getting top-level branch when a single key was provided
        if not isinstance(keys, tuple):
            keys = (keys,)

        branch = self.dict
        for key in keys:
            branch = branch[key]

        # If we return a branch, and not a leaf value, we wrap it into a NestedDict
        return NestedDict(branch) if isinstance(branch, dict) else branch

    def __setitem__(self, keys, value):
        # Allows setting top-level item when a single key was provided
        if not isinstance(keys, tuple):
            keys = (keys,)

        branch = self.dict
        for key in keys[:-1]:
            if not key in branch:
                branch[key] = {}
            branch = branch[key]
        branch[keys[-1]] = value

Here are examples of usage

# Getting an item
my_dict = NestedDict({'a': {'b': 1}})
my_dict['a', 'b'] # 1

# Setting an item
my_dict = NestedDict()
my_dict[1, 2, 3] = 4
my_dict.dict # {1: {2: {3: 4}}}

# You can even get a branch
my_dict[1] # NestedDict({2: {3: 4}})
my_dict[1][2, 3] # 4

You can then make NestedDict implementation richer by also defining __iter__, __len__ and __contains__.

Also, this can be integrated fairly easily in your code since any pre-existing dictionary can be turned into a nested one by doing NestedDict(your_dict).

like image 117
Olivier Melançon Avatar answered Oct 16 '22 06:10

Olivier Melançon


Make traversal a method of your dictionary. You can do this easily by subclassing dict.

The algorithm for traversing is courtesy of @MartijnPeters (upvote there).

import operator

class ndict(dict):

    def get_traverse(self, mapList):
        return reduce(operator.getitem, mapList, self)

    def set_traverse(self, mapList, value):
        self.get_traverse(mapList[:-1])[mapList[-1]] = value

d = ndict({'a': {'b': 'c'}, '1': {'2': {'3': {'4': '5'}}}})

d.get_traverse(['a', 'b'])     # 'c'
d.set_traverse(['a', 'b'], 4)  # {'a': {'b': 4}, '1': {'2': {'3': {'4': '5'}}}}
like image 42
jpp Avatar answered Oct 16 '22 07:10

jpp