update values in nested dict based on key - python

I have a nested dictionary and I want to update the value of an item based on its key value.
For example, I have the following dictionary and I want to set the value of every occurrence of an item with key=='p' to be 1.
my_dict = {'p': 0, 'nested_dict': {'p': 0, 'a': 2}}
For a regular dictionary (non-nested) the update method provides a simple one-liner solution:
my_dict.update((x, 1) for x, y in my_dict.items() if x=='p')
I'm looking for a similar solution for the case of nested dictionary

If the max depth of the dictionary is known and constant and the 'p's always appear at the same depth this can be solved without recursion. In all other cases, a recursive approach is needed. Based on the example problem and your comments, I will assume that the second one is true. Here is a recursive solution:
def setPto1(dct):
return {k: setPto1(v) if isinstance(v, dict) else [v, 1][k=='p'] for k, v in dct.items()}
So you basically loop through the key-value pairs with a recursive dictionary comprehension. You were not far off with your approach. If there is something you do not understand about it, leave a comment.
If [v, 1][k=='p'] makes you feel uneasy, you can replace it with the more straight-forward (1 if k=='p' else v). They are the same thing.

Maybe this could get you started (you can still make it more generic, and it might have problems with repeated keys):
from functools import reduce
def decision_func(d, item, replace_on_item, replace_val):
if item == replace_on_item:
d[item] = replace_val
else:
return d[item]
my_dict = {'p': 0, 'nested_dict': {'p': 0, 'a': 2}}
reduce(lambda d, item: decision_func(d, item, 'p', ["some_other_val", 2]), ['nested_dict', 'p'], my_dict)
print(my_dict)
Output:
{'p': 0, 'nested_dict': {'p': ['some_other_val', 2], 'a': 2}}
What is done here is that you are going to run down the nesting tree by using the reduce function, and the decision_func is the one making the decision on whether to update a given entry or return the value for that key and continue looking for the next nesting level (basically a recursion prob). Note that as written above, this is not yet a bullet proof implementation.

Related

Get keys by list of values

I have a dictionary and a list of values such as:
dictionary = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
liste = [2, 3]
I would like:
result = ['b', 'c']
If I have a very large dictionary, what is the most optimal way to do this?
The keys have unique values.
The idea here is to create a reverse_dict for an efficient lookup, otherwise the complexity can be O(mn), m-number of keys, n-length of liste. A value can be duplicate, so keeping a list for the keys is also important.
result = []
reverse_dict = collections.defaultdict(list)
for key, value in dictionary.items():
reverse_dict[value].append(key)
for v in liste:
result.extend(reverse_dict[v])
dictionary = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
liste = [2, 3]
result = []
for key, value in dictionary.items():
if value in liste:
result.append(key)
As a list comprehension:
result = [key for key, value in dictionary.items() if value in liste]
Lookup by value is exactly what dictionaries are not meant for.
Since you said the dictionary is very large, the most efficient solution relies on how many times you're operating on it.
If it's going to be a frequent task, you may need to create a reverse dictionary. This can be done comprehensively:
rev_dict = {v: k for k, v in dictionary.items()}
Then you can lookup the dictionary the way it's designed for.
On the contrary, an isolated case does not justify creating a copy of a very large dictionary, which can be memory and time consuming. So I came up with this awful messy un-Pythonic construct, which exploits the ordered feature of Python 3.7+ dictionaries:
list(dictionary.keys())[list(dictionary.values()).index(your_value)]
Mind you, use this only if you're desperate.
Obviously, the best solution is the one everybody knows but no-one wants: hardcode a reverse dictionary before running the script.
This can also bring some issues to your attention that the above solutions are unable to avoid at runtime (not without being explicitly handled), e.g. reversing your dict may result in having duplicate keys, which is not illegal, but will result in a sadly shortened dictionary.

Efficient way to add a new key-value to a nested dictionary in Python?

I have a very large nested dictionary and below I am showing a sample of it.
tmp_dict = {1: {'A': 1, 'B': 2},
2: {'A': 0, 'B': 0}}
The question is what is any better/efficient way to add a new pair key value to my existing nested dict. I am currently looping through the keys to do so. Here is an example:
>>> for k in tmp_dict.keys():
tmp_dict[k].update({'C':1})
A simple method would be like so:
for key in tmp_dict:
tmp_dict[key]['C']=1
Or, you could use dictionary comprehension, as sushanth suggested
tmp_dict = {k: {**v, 'C': 1} for k, v in timp_dict.items()}
You can read more about the asterisks (and why this works) here.
In terms of complexity, they are all O(N) time complexity (I think the dict comprehension maybe O(N^2)). So, your solution should have a relatively quick run time anyways.

Swapping dictionary keys and values works only on 3 key-value pair dictionary

Here's a function that is supposed to swap dictionary keys and values. {'a': 3} is supposed to become {3: 'a'}.
def change_keys_values(d):
for key in d:
value = d[key]
del d[key]
d[value] = key
return d
I've realized that this function shouldn't work because I'm changing dictionary keys during iteration. This is the error I get: "dictionary keys changed during iteration". However, I don't get this error on a three key-value pair dictionary. So, while {'a': 3, 't': 8, 'r': 2, 'z': 44, 'u': 1, 'b': 4} results in the above mentioned error, {'a': 3, 't': 8, 'r': 2} gets solved without any issues. I'm using python 3. What is causing this?
You must never modify a dictionary inside a loop. The reason is the way the dictionaries are often implemented.
Hash Tables
Basically, when you create a dictionary, each item is indexed using the hash value of the key.
Dictionaries are implemented sparsely
Another implementation detail involves the fact that dictionaries are implemented in a sparse manner. Namely, when you create a dictionary, there are empty places in memory (called buckets). When you add or remove elements from a dictionary, it may hit a threshold where the dictionary key hashes are re-evaluated and as a consequence, the indexes are changed.
Roughly speaking, these two points are the reason behind the problem you are observing.
Moral Point: Never modify a dictionary inside a loop of any kind.
Here's a simple code to do what you want:
def change_keys_values(d):
new_dict = {value: key for key, value in d.items()}
return new_dict
You need to verify that the values are unique, after that, no problem :)
But be sure not to change a dictionary while parsing it. Otherwise, you could encounter an already changed index that get's interpreted twice or even more. I suggest making a new variable (a copy):
def invert(dict_: dict) -> dict:
if list(set(dict_.values())) == list(dict_.values()): # evaluates if "inverting key:value" is possible (if keys are unique)
return {b: a for a, b in dict_.items()}
else:
raise ValueError("Dictionary values contain duplicates. Inversion not possible!")
print(invert({"a": 1, "b": 2, "c": 3, "d": 4})) # works
print(invert({"a": 1, "b": 2, "c": 3, "d": 3})) # fails
To fix your issue, just iterate over copy, not the original dict:
import copy
def change_keys_values(d):
for key in copy.deepcopy(d):
value = d[key]
del d[key]
d[value] = key
return d
Then the good alternative using zip would be:
def change_keys_values(d):
a, b = zip(*d.items())
d = dict(list(zip(b,a)))
return d

How do I remove key values in a dictionary?

Python language.
I know how to remove keys in a dictionary, for example:
def remove_zeros(dict)
dict = {'B': 0, 'C': 7, 'A': 1, 'D': 0, 'E': 5}
del dict[5]
return dict
I want to know how to remove all values with zero from the dictionary and then sort the keys alphabetically. Using the example above, I'd want to get ['A', 'C', 'E'] as a result, eliminating key values B and D completely.
To sort do I just use dict.sort() ?
Is there a special function I must use?
sorted(k for (k, v) in D.iteritems() if v)
Sometimes when you code you have to take a step back and try to go for your intent, rather than trying to do one specific thing and miss the entire big picture. In python you have this feature called list/dictionary comprehension, from which you can use to filter the input to get the results you desire. So you want to filter out all values in your dictionary that are 0, it's simply this:
{k, v for k, v in d.items() if v != 0}
Now, dictionaries are hash tables, by default they are not sortable, however there is a class that can help you with this in collections. Using the OrderedDict class to facilitate the sorting, the code will end up like this:
OrderedDict(sorted(((k, v) for k, v in d.items() if v != 0)), key=lambda t: t[0])
Also, it's highly inadvisable to name your variables with the same name as a builtin type or method, such as dict.

Recursive dictionary modification in python

What would be the easiest way to go about turning this dictionary:
{'item':{'w':{'c':1, 'd':2}, 'x':120, 'y':240, 'z':{'a':100, 'b':200}}}
into this one:
{'item':{'y':240, 'z':{'b':200}}}
given only that you need the vars y and b while maintaining the structure of the dictionary? The size or number of items or the depth of the dictionary should not matter, as the one I'm working with can be anywhere from 2 to 5 levels deep.
EDIT: I apologize for the type earlier, and to clarify, I am given an array of strings (eg ['y', 'b']) which I need to find in the dictionary and then keep ONLY 'y' and 'b' as well as any other keys in order to maintain the structure of the original dictionary, in this case, it would be 'z'
A better example can be found here where I need Chipset Model, VRAM, and Resolution.
In regards to the comment, the input would be the above link as the starting dictionary along with an array of ['chipset model', 'vram', 'resolution'] as the keep list. It should return this:
{'Graphics/Displays':{'NVIDIA GeForce 7300 GT':{'Chipset Model':'NVIDIA GeForce 7300 GT', 'Displays':{'Resolution':'1440 x 900 # 75 Hz'}, 'VRAM (Total)':'256 Mb'}}
Assuming that the dictionary you want to assign to an element of a super-dictionary is foo, you could just do this:
my_dictionary['keys']['to']['subdict']=foo
Regarding your edit—where you need to eliminate all keys except those on a certain list—this function should do the trick:
def drop_keys(recursive_dict,keep_list):
key_list=recursive_dict.keys()
for key in key_list:
if(type(recursive_dict[key]) is dict):
drop_keys(recursive_dict[key], keep_list)
elif(key not in keep_list):
del recursive_dict[key]
Something like this?
d = {'item': {'w': {'c': 1, 'd': 2}, 'x': 120, 'y': 240, 'z': {'a': 100, 'b': 200}}}
l = ['y', 'z']
def do_dict(d, l):
return {k: v for k, v in d['item'].items() if k in l}
Here's what I arrived at for a recursive solution, which ended up being similar to what #Dan posted:
def recursive_del(d,keep):
for k in d.copy():
if type(d[k]) == dict:
recursive_del(d[k],keep)
if len(d[k]) == 0: #all keys were deleted, clean up empty dict
del d[k]
elif k not in keep:
del d[k]
demo:
>>> keepset = {'y','b'}
>>> a = {'item':{'w':{'c':1, 'd':2}, 'x':120, 'y':240, 'z':{'a':100, 'b':200}}}
>>> recursive_del(a,keepset)
>>> a
{'item': {'z': {'b': 200}, 'y': 240}}
The only thing I think he missed is that you will need to sometimes need to clean up dicts which had all their keys deleted; i.e. without that adjustment you would end up with a vestigial 'w':{} in your example output.
Using your second example I made something like this, it's not exactly pretty but it should be easy to extend. If your tree starts to get big, you can define some sets of rules to parse the dict.
Each rule here are actually pretty much "what should I do when i'm in which state".
def rule2(key, value):
if key == 'VRAM (Total)':
return (key, value)
elif key == 'Chipset Model':
return (key, value)
def rule1(key, value):
if key == "Graphics/Displays":
if isinstance(value, dict):
return (key, recursive_checker(value, rule1))
else:
return (key, value)
else:
return (key, recursive_checker(value, rule2))
def recursive_checker(dat, rule):
def inner(item):
key = item[0]
value = item[1]
return rule(key, value)
return dict(filter(lambda x: x!= None, map(inner, dat.items())))
# Important bits
print recursive_checker(data, rule1)
In your case, as there is not many states, it isn't worth doing it but in case you have multiple cards and you don't necessarly know which key should be traversed but only know that you want certain keys from the tree. This method could be used to search the tree easily. It can be applied to many things.

Categories