How to dynamically move inside a nested dict in Python - python

I have a dict with a dynamical number of nested dicts inside of it, something like:
my_dict = {"a": {"b": {"c: {...}}}}
I need to dynamically move inside this dict, for instance I'd like to do the following:
levels = ["a", "b", "c"]
my_dict[levels[0]][levels[1]][levels[2]] = "something"
where the number of items inside "levels" may vary.
I can partially achieve the same result for a limited number of items inside "levels" by writing something like this:
if len(levels) == 1:
my_dict[levels[0]] = "something"
elif len(levels) == 2:
my_dict[levels[0]][levels[1]] = "something"
elif len(levels) == 3:
my_dict[levels[0]][levels[1]][levels[2]] = "something"
(...)
but I'm looking for a more general and elegant solution.
Is there a way to do this?

There isn't a lot of code here to go on, but for what you have given, you can define
def get(d, keys):
for key in keys:
d = d[key]
return d
def set(d, keys, value):
d = get(d, keys[:-1])
d[keys[-1]] = value
And then use it like this
my_dict = {"a":{"b":{"c":{}}}}
set(my_dict, ["a", "b", "c"], "something")
print get(my_dict, ["a", "b", "c"])
A functional alternative for get:
def get(d, keys):
return reduce(lambda d, key: d[key], keys, d)

Related

Converting a dataset from one for to another

I have one dataset in a list form and I want to convert it into another dataset under certain conditions.
Conditions
"a" = 1
"b" = 2
"c" = 3
input_list = ["a", "b", "c"]
# something happens
output_list = [1, 2, 3]
What to do?
Represent your set of conditions in a dictionary:
conditions = {
"a": 1,
"b": 2,
"c": 3
}
Use that dictionary in order to generate the output:
input_list = ["a", "b", "c"]
output_list = [conditions[x] for x in input_list]
What you want to achieve is a mapping.
Your conditions are a map, dictionary (Python lingo) or hash-table.
Each value there (like 1) corresponds to a key in the dataset (like "a").
To represent this mapping you can use a table-like datastructure that maps a key to a value. In Python this datastructure is called a dictionary:
mapping = {"a": 1, "b": 2, "c": 3} # used to translate from key to value (e.g. "a" to 1)
input_list = ["a", "b", "c"]
# something happens
output_list = []
for v in input_list:
mapped_value = mapping[v] # get the corresponding value, translate or map it
print(v + " -> " + mapped_value)
output_list.append(mapped_value)
print(output_list) # [1, 2, 3]
See also
Map (mathematics) - Wikipedia
Convert numbers into corresponding letter using Python

Is it possible to edit sub element in iterable object recursively

I get some override elements providen with this kind of structure:
(keys, value)
I would like to use that to change value in nested object (dict and list). It could look like this:
data[keys[0]][keys[1]][keys[2]] = value
The hard point is, the number of keys can be variable. I have no clue how to manage this recursively.
A functional approach using functools.reduce and operator.getitem
from functools import reduce
from operator import getitem
data = {"a": {"b": {"c": 1}}}
keys = ["a", "b", "c"]
*suffix, key = keys
reduce(getitem, suffix, data)[key] = 2
print(data)
Output
{'a': {'b': {'c': 2}}}
You can do the following (iteratively):
def setkeys(data, keys, value):
*path, last = keys
for key in path:
data = data[key]
data[last] = value
Or for a recursive solution:
def setkeys(data, keys, value):
head, *tail = keys
if tail:
setkeys(data[head], tail, value)
else:
data[head] = value

Iterate adding values to an existing dictionary by using a variety of keys stored as a list

I had trouble coming up with an appropriate title for this, so apologies there.
I have an existing dictionary di_end which already has an order to its keys. I also have some objects which have a property containing the keys for where in di_end the user-entered value will go
Note: the methods setProperty() and property() are from the pyqt library where setProperty() creates a custom property for an object where the first argument is the name of the property and the second argument is the value for that named property and property() just returns the values for whatever name is passed into it as an argument.
Something like this:
a.setProperty('keys', [key1, key2, key3])
b.setProperty('keys', [key4, key5, key6, key7, key8])
c.setProperty('keys', [key9])
objects_list = [a, b, c]
I want to be able to use the keys stored in the object properties to load the value that the user enters into a field to a dictionary
I'd like to iterate the process such that these parts
di_end[a.property(['keys'])[0]][a.property(['keys'])[1]][a.property(['keys'])[2]] = a.value
di_end[b.property(['keys'])[0]][b.property(['keys'])[1]][b.property(['keys'])[2]]\
[b.property(['keys'])[3]][b.property(['keys'])[4]] = b.value
di_end[c.property(['keys'])[0]] = c.value
or
a_li, b_li, c_li = a.property(['keys']), b.property(['keys']), c.property(['keys'])
di_end[a_li[0]][a_li[1]][a_li[2]] = a.value
di_end[b_li[0]][b_li[1]][b_li[2]][b_li[3]][b_li[4]] = b.value
di_end[c_li[0]] = c.value
do not need to be manually typed out and could be performed procedurally. I think I could do this if it was the same amount of keys but I'm not sure how to do it with differing amounts of keys. If they were all the same amounts I'd just do this
a.setProperty('keys', [key1, key2, key3, key4])
b.setProperty('keys', [key5, key6, key7, key8])
c.setProperty('keys', [key9, key10, key11, key12])
objects_list = [a, b, c]
a_li, b_li, c_li = a.property(['keys']), b.property(['keys']), c.property(['keys']) # assuming all are 4 entires each
for count, item in enumerate([a_li, b_li, c_li]):
di_end[item[0]][item[1]][item[2]][item[3]] = objects_list[count].value
but since there are different amounts of keys for each entry, I'm not sure how to accomplish this.
Edit: Added a note about setProperty() and property()
It's pretty hard to tell what your code is doing here, mostly due to the setProperty and property objects, which seem awkward and not-pythonic.
That being said, this looks like yet another use case for the excellent glom library (pip install glom).
from glom import assign, Path
assign(data, Path(*keys), value)
Note that in your case
data = di_end
keys = a.property['keys']
value = a.value
and an example:
>>> data = {'a': {'b': {'c': 5}}}
>>> keys = ['a', 'b', 'c']
>>> value = 100
>>> assign(data, Path(*keys), value)
{'a': {'b': {'c': 100}}}
>>> data
{'a': {'b': {'c': 100}}}
Note that this will raise a KeyError if the path doesn't already exist. For the generate-dict-on-demand (like a infinite nested defaultdict) style, you need to use Assign instead:
glom(data, Assign(Path(*keys), value))
See https://glom.readthedocs.io/en/latest/mutation.html for more details.
It seems you can try use recursion here. A simple example below.
put function creates nested dict and put value. fetch function can extract value from nested dict.
def fetch(data, keys):
if len(keys) == 1:
return data[keys[0]]
return fetch(data[keys.pop()], keys)
def put(data, keys, value):
if len(keys) == 1:
data[keys[0]] = value
return
key = keys.pop()
data[key] = {}
put(data[key], keys, value)
di_empty = {}
keys_values = [
(("a", "b", "c"), "abc"),
(("b", "c", "d", "e"), "bcde")
]
for selected in keys_values:
put(di_empty, list(reversed(selected[0])), selected[1])
di_end = dict()
di_end["a"] = {"b": {"c": "abc"}}
di_end["b"] = {"c":{"d": {"e": "bcde"}}}
keys = [("a", "b", "c"), ("b", "c", "d", "e")]
for selected in keys:
s1 = fetch(di_end, list(reversed(selected)))
s2 = fetch(di_empty, list(reversed(selected)))
assert s1 == s2
print(s1)
print(s2)

Function to check if N number of keys exist in a dictionary

I am looking to create a function that will check if multiple keys are present in a dictionary and return a value that indicates which keys were present. I know I could write something like:
def param_checker(parameters):
if 'foo' in parameters:
if 'bar' in parameters:
if 'qux' in parameters:
return 0
else:
return 1
elif 'baz' in parameters:
if 'qux' in parameters:
return 2
else:
return 3
else:
return 4
elif 'quxx' in parameters:
if 'bar' in parameters:
return 5
elif 'qux' in parameters:
if 'baz' in parameters:
return 6
else:
return 7
else:
return 8
else:
return 9
However, the above example is both messy and clunky. The complexity also scales with the number of parameters you need to check to return a value. For example, I needed 3 layers of if conditions to check the keys foo, bar, and qux. I know I could chain the if conditions inline, but it is still messy and clunky.
This is somewhat similar to Python check if list of keys exist in dictionary. The big difference here is that instead of checking if they all exist or not, I am looking to return a value indicating what combination of keys exist.
Note: I did not write out all possible combinations in the provided sample code, as that would be tedious when I am just trying to illustrate the problem.
Below is a example of invoking the function to check which keys are present.
d = dict()
d['foo'] = 'blah'
d['bar'] = 'blah'
val = param_checker(d)
# val represents a unique value indicating which keys are present in dictionary d
I will know ahead of time what keys could be in the dictionary, so I can have some sort of list of possible keys, like:
keys = ["foo", "bar", "baz", "qux", "quxx"]
I am hoping to build a function that can check N keys and return a unique value indicating which ones where present. How can I efficiently do this?
keyseq = ('foo', 'bar', 'quux')
keymap = {key: 1 << e for (e, key) in enumerate(keyseq)}
return sum(keymap[key] for key in keymap if key in D)
Something like this (assuming Python 3)?
from itertools import compress
queries = ["a", "b", "c", "d"]
in_dict = [q in d for q in queries]
existing_keys = compress(queries, in_dict)
You can of course wrap this in a function where queries become the input parameter and d is the input dictionary.
Make a set of your desired keys.
Make a set of the dict's keys.
Take the intersection. Count.
master = {
"foo": 1,
"bar": 5,
"qux": 18,
"lemon": -6,
"quxx": "How boring are examples?",
"dog": False
}
tag = {
"foo", "bar", "baz", "qux", "quxx"
}
master_set = set(master.keys())
found = master_set.intersection(tag)
print (len(found), found)
Output:
4 {'quxx', 'foo', 'qux', 'bar'}
You can try this:
keys = ["foo", "bar", "baz", "qux", "quxx"]
counter = {i:d.keys().count(i) for i in keys}

Iterate over a list inside a nested dictionary

Lets say I have a dictionary like this:
myDict = {
1: {
"a": "something",
"b": [0, 1, 2],
"c": ["a", "b", "c"]
},
2: {
"a": "somethingElse",
"b": [3, 4, 5],
"c": ["d", "e", "f"]
},
3: {
"a": "another",
"b": [6, 7, 8],
"c": ["g", "h", "i"]
}
}
And this is my code:
for id, obj in myDict.items():
for key, val in obj.items():
if key is "b":
for item in val:
# apply some function to item
Is there a better way to iterate over a list inside a nested dict? Or is there a pythonic way to do this?
You absolutely do not need to iterate the list to print it (unless this is a functional requirement for the code you are writing).
Very simply, you could do this:
for id, obj in myDict.items():
if "b" in obj:
print obj["b"]
To map the list object, represented by obj['b'] to another function, you can use the map function:
map(foo, obj["b"])
If you're dictionary is always two levels deep, I don't see anything wrong with your approach. In your implementation, I would use key == "b" rather than key is "b". Using is will test for identity (e.g. id(a) == id(b)), while == will test for equality (e.g. a.__eq__(b)). This functions the same way when I test it in IDLE, but it's not a good habit to get into. There's more info on it here: How is the 'is' keyword implemented in Python?
If you want to deal with varying level dictionaries, you could use something like:
def test_dict_for_key(dictionary, key, function):
for test_key, value in dictionary.items():
if key == test_key:
dictionary[key] = type(value)(map(function, value))
if isinstance(value, dict):
test_dict_for_key(value, key, function)
An example usage might be something like:
myDict = {
1: {
"a": "something",
"b": [0, 1, 2],
"c": ["a", "b", "c"]
},
2: {
"a": "somethingElse",
"b": [3, 4, 5],
"c": ["d", "e", "f"]
},
3: {
"a": "another",
"b": [6, 7, 8],
"c": ["g", "h", "i"]
}
}
# adds 1 to every entry in each b
test_dict_for_key(myDict, "b", lambda x: x + 1)
# prints [1, 2, 3]
print(myDict[1]["b"])
I'm a fan of generator expressions.
inner_lists = (inner_dict['b'] for inner_dict in myDict.values())
# if 'b' is not guaranteed to exist,
# replace inner_dict['b'] with inner_dict.get('b', [])
items = (item for ls in inner_lists for item in ls)
Now you can either use a foo loop
for item in items:
# apply function
or map
transformed_items = map(func, items)
A couple fixes could be made.
Don't use is when comparing two strings (if key is "b":)
Simply say print(item) instead of using .format(), since you only have one variable that you're printing, with no additional string formatting
Revised code:
for id, obj in myDict.items():
for key, val in obj.items():
if key == "b":
for item in val:
print(item)
If you are sure that you will have a b key in every case, you can simply do:
for id, obj in myDict.items():
for item in obj["b"]:
print item

Categories

Resources