Mathieu
Mathieu

Reputation: 5766

Access nested dictionary through a variable

I have the following setup: a function returns a dictionary with N timelines of equal size (100k points). The dictionary returns looks like:

timelines = dict()
timelines["Name1"] = dict()
timelines["Name1"]["Name2"] = dict()
timelines["Name1"]["Name3"] = dict()
timelines["Name1"]["Name2"]["a"] = # List of 100k points
timelines["Name1"]["Name2"]["b"] = # List of 100k points
timelines["Name1"]["Name2"]["c"] = # List of 100k points
timelines["Name1"]["Name3"]["b"] = # List of 100k points
timelines["Name1"]["Name2"]["c"] = # List of 100k points
timelines["Name1"]["a"] = # List of 100k points
timelines["Name1"]["b"] = # List of 100k points
timelines["Name2"] # and so on.

As you might have understood, the timelines (list of points) are not always stored in the same level. Sometimes I can access it with 1 key, sometimes with 2, sometimes with 5. Those keys will give me the labels for the plot and are necessary. My plan was to pass to the plot function a tuple of the keys.

Example:

T = ("Name1", "Name2", "b") 
# Will allow me to access the timeline:
timelines["Name1"]["Name2"]["b"]
# by doing:
timelines[T[0]][T[1]][T[2]]

In the example above, I wrote the dictionary path myself ([T[0]][T[1]][T[2]]), however how can I acess the right timeline with a tuple T of unknown size? How can I unpack the tuple into a dictionary path?

Thanks :)

Upvotes: 3

Views: 1753

Answers (4)

gold_cy
gold_cy

Reputation: 14236

I would actually do it like this, this will be the fastest method more than likely

from functools import reduce
from operator import getitem

path = ['Name1', 'Name2', 'a']

reduce(getitem, path, dictionary)

Lambda calls can become expensive especially as data grows, not to mention getitem will be faster than any other method listed here because it's implemented purely in C

Upvotes: 10

balderman
balderman

Reputation: 23825

Traverse the data structure like this:

timelines = dict()
timelines["Name1"] = dict()
timelines["Name2"] = dict()
timelines["Name1"]["Name2"] = dict()
timelines["Name1"]["Name3"] = dict()
timelines["Name1"]["Name2"]["a"] = []  # List of 100k points
timelines["Name1"]["Name2"]["b"] = [12, 67, 45]  # List of 100k points
timelines["Name1"]["Name2"]["c"] = [12, 45]  # List of 100k points
timelines["Name1"]["Name3"]["b"] = []  # List of 100k points
timelines["Name1"]["Name2"]["c"] = []  # List of 100k points
timelines["Name1"]["a"] = []  # List of 100k points
timelines["Name1"]["b"] = []  # List of 100k points
timelines["Name2"]["Name23"] = dict()
timelines["Name2"]["Name23"]["Z"] = []  # and so on.


def get_time_line(path):
    data = timelines
    for path_element in path:
        data = data[path_element]
    return data

path = ("Name1", "Name2", "b")
print(get_time_line(path))

Output:

[12, 67, 45]

Upvotes: 1

kederrac
kederrac

Reputation: 17322

you can use a recursive approach

def access_my_dict(my_dict, tuple_keys):
    value = my_dict[tuple_keys[0]]
    if isinstance(value, dict):
        return access_my_dict(value, tuple_keys[0:])
    return value

Upvotes: 1

Thierry Lathuille
Thierry Lathuille

Reputation: 24288

You could use reduce

from functools import reduce

timelines = dict()
timelines["Name1"] = dict()
timelines["Name1"]["Name2"] = dict()
timelines["Name1"]["Name2"]["b"] = 'my data'

T = ("Name1", "Name2", "b") 

data = reduce(lambda d, key: d[key], T, timelines)

print(data)
# my data

The initial value is your timeline dict. Reduce iterates on your T tuple, and at each iteration, calls the lambda function. The lambda takes two arguments: the first one is the accumulated value, in this case the current dict d, and the second one is the key it got from the tuple during this iteration. The lambda returns d[key], which becomes the new accumulated value for the next iteration.

Upvotes: 2

Related Questions