Python navigating nested dictionary - python-3.x

I have a nested dictionary that might have non-unique keys
I need to dynamically add/get key-value pairs on this dictionary by their key in string format
The string that is the key name is being read from keyboard input in string format
Given that string I have to find a key that matches it and do something with a corresponding key-value pair
The keys might be non-unique among different nesting levels
But they are unique on a single nesting level
For example, I have this dict:
MyDict = {'a': 1, 'b': 2}
Now, I get some input in string format that tells me to add one more nesting level with key 'c' and then fill it with a key-value pair 'a:3'. I do it like that:
last_edited_element = {'a': 3}
MyDict['c'] = last_edited_element
#MyDict is now {'a': 1, 'b': 2, 'c': {'a': 3}}
Then, I get an input that tells me to do something with a key-value pair that has 'a' key
I am given a rule that first I have to look up for 'a' key in a level that was edited last, then If nothing is found - go one level up. I store my last edited element in a variable last_edited_element.
The last edited element was {'a': 3}, so I do:
if 'a' in last_edited_element:
#do something with {'a': 3}
else:
#go one level up and look for 'a' key there
So the question is, how do I go one level up? I have {a: '3'} stored in a variable last_edited_element, I need to access the upper-level dictionary if any that contains last_edited_element, something like last_edited_element.get_parent_dictionary()
How do I do that?

So following up on my first comment, you could do this:
Initialize a top-level dict
Initialize all children to refer to their parent
Recursively search bottom-up until you find a key or None
example_dict = {'a': 1, 'b': 2, 'parent': None}
example_dict['c'] = {'a': 3, 'parent': example_dict}
def find_key(k, d):
if k in d:
return d[k]
elif d['parent'] is None:
return None
return find_key(k, d['parent'])
print(find_key('a', example_dict['c']))
> 3
print(find_key('b', example_dict['c']))
> 2

Related

variable in string of dictionary in python

I have the following dictionary in python 3:
a = {'a': 'b:{androidString}'}
Try to define a variable and use it in the dictionary:
android = "androidString"
a = {'a': f'b:{{android}}'}
when:
print(a)
it says:
{'a': 'b:{android}'}
how to use the variable in the dictionary?
You're missing a curly bracket :
a = {'a': f'b:{{{android}}}'}
Output
{'a': 'b:{androidString}'}
Just use get method:
...
a.get('a')
get method takes two parameters first key name , second default
default returns Errors in default but you can change it to False
so when you try to get key doesn't exists it will return default
or you can use []:
...
a['a']
there are a lot options to get key of dictionary More info

How to replace the items from given dictionary?

I am a newbie in python. I was playing with dictionaries and wanted to know the solution to the given problem
list_ = [['any', 'window', 'says', 'window'], ['dog', 'great'], ['after', 'end', 'explains', 'income', '.', '?']]
dictionary=[('dog', 'cat'), ('window', 'any')]
def replace_matched_items(word_list, dictionary):
int_word = []
int_wordf = []
for lst in word_list:
for ind, item in enumerate(lst):
for key,value in dictionary:
if item in key:
lst[ind] = key
else:
lst[ind] = "NA"
int_word.append(lst)
int_wordf.append(int_word)
return int_wordf
list_ = replace_matched_items(list_, dictionary)
print(list_ )
Output generated is:
[[['NA', 'window', 'NA', 'window'], ['NA', 'NA'], ['NA', 'NA', 'NA', 'NA', 'NA', 'NA']]]
The expected output is:
[[['NA', 'window', 'NA', 'window'], ['dog', 'NA'], ['NA', 'NA', 'NA', 'NA', 'NA', 'NA']]]
I am using python 3
Thanks in advance
Some quick introduction to data structures in python just to clarify your question.
A list is similar to your arrays, where they can be accessed via their index and are mutable meaning items within the list can be changed. Lists are generally identified by brackets [].
For example:
my_array = [4, 8, 16, 32]
print(my_array[0]) # returns 4
print(my_array[3]) # returns 32
my_array[2] = 0
print(my_array) # returns [4, 8, 0, 32]
A tuple is similar to a list, however, the main difference is that they are immutable meaning items within the tuple cannot be changed. Items can still be accessed via their index. They are generally identified by parenthesis ().
For example:
my_tuple = ('this', 'is', 'a', 'tuple', 'of', 'strings')
print(my_tuple[0]) # returns 'this'
my_tuple[1] = 'word' # throws a 'TypeError' as items within tuples cannot be changed.
A dictionary uses a key and value pair to access, store, and change data in a dictionary. Similar to lists, they are mutable, however, each value has their own unique key. To access a value in the dictionary, you have to pass the key within the dictionary. Dictionaries are generally identified by curly braces {}.
For Example:
my_dictionary = {'John':13, 'Bob':31, 'Kelly':24, 'Ryan':17}
print(my_dictionary['Kelly']) # Returns 24
my_dictionary['Kelly'] = 56 # Assigns 56 to Kelly
print(my_dictionary['Kelly']) # Returns 56
The key:value takes this form within the dictionary, and each subsequent key-value pairs are separated by commas.
I would highly suggested reading the official documentation on the Data Structures available for python: Link Here
To answer the question
From your code given, you've used a tuple with your key-value pair encapsulated the tuple in a list as your dictionary data structure.
Your expected output is a result of you iterating through the entire dictionary, and did not handle what will occur once you've found the key for your dictionary. This can be fixed by adding a break statement within your if-statement once a key has been found.
The break statement, will exit your for loop once a key has been found and, will continue onto the next list item.
Your function will end up looking like:
def replace_matched_items(word_list, dictionary):
int_word = []
int_wordf = []
for lst in word_list:
for ind, item in enumerate(lst):
for key,value in dictionary:
if item in key:
lst[ind] = key
break
else:
lst[ind] = "NA"
int_word.append(lst)
int_wordf.append(int_word)
return int_wordf
Suggested to use a Dictionary
Using a Dictionary data structure for your key and value pairs will let you have access to methods that'll let you check whether a key exists inside your dictionary.
If you have a list of keys, and you'd like to check if a dictionary key exists within a list:
this_list = ['any', 'window', 'says', 'window', 'dog',
'great', 'after', 'end', 'explains', 'income', '.', '?']
dictionary = {'dog':'cat', 'window':'any'}
matched_list = []
for keys in dictionary:
if keys in this_list:
matched_list.append(keys) # adds keys that are matched
else:
# do something if the key is in not the dictionary
print(matched_list)
# Returns ['dog', 'window']

Deleting Dictionaries Keys with a For If Statement in Python 3

I feel very dumb asking this. How do I delete a keys in a dictionary with an if statement that references the values. When I do this:
newdict = {"a":1,"b":2,"c":3}
for (key,value) in newdict:
if value == 2:
del newdict[key]
print(newdict)
It throws this error:
line 3, in <module>
for (key,value) in newdict:
ValueError: not enough values to unpack (expected 2, got 1)
Thank you.
If you need to delete based on the items value, use the items() method or it'll give ValueError. But remember if you do so it'll give a RuntimeError. This happens because newdict.items() returns an iterator not a list. So, Convert newdict.items() to a list and it should work. Change a bit of above code like following -
for key,value in list(newdict.items()):
if value == 2:
del newdict[key]
output -
{'a': 1, 'c': 3}

Adding dictionary values to a list?

I have the following:
list_of_values = []
x = { 'key1': 1, 'key2': 2, 'key3': 3 }
How can I iterate through the dictionary and append one of those values into that list? What if I only want to append the value of 'key2'.
If you only want to append a specific set of values you don't need to iterate through the dictionary can simply add it to the list
list_of_values.append(x["key2"])
However, if you insist on iterating through the dictionary you can iterate through the key value pairs:
for key, value in x.items():
if key == "key2":
list_of_values.append(value)
If you really want to iterate over the dictionary I would suggest using list comprehensions and thereby creating a new list and inserting 'key2' :
list_of_values = [x[key] for key in x if key == 'key2']
because that can be easily extended to search for multiple keywords:
keys_to_add = ['key2'] # Add the other keys to that list.
list_of_values = [x[key] for key in x if key in keys_to_add]
That has the simple advantage that you create your result in one step and don't need to append multiple times. After you are finished iterating over the dictionary you can append the list, just to make it interesting, you can do it without append by just adding the new list to the older one:
list_of_values += [x[key] for key in x if key in keys_to_add]
Notice how I add them in-place with += which is exactly equivalent to calling list_of_values.append(...).
list_of_values = []
x = { 'key1': 1, 'key2': 2, 'key3': 3 }
list_of_values.append(x['key2'])

Python3.4 Dictionary value replacement issue

I have some code which takes a list of dictionaries and creates another list of dictionaries.
Each dictionary in the list has two key/value pairs "ID" and "opcode", where "opcode" is a 32 bit number.
My code needs to create a second list of dictionaries where the opcodes are separated, i.e. a dictionary with opcode=5 would become two dictionaries with opcode=1 and opcode=4.
(opcode is a 32 bit number and my requirement is that only 1 bit is high, ie opcode=1,2,4,8,16 etc)
I've simplified the problem into the following; my code needs to turn this:
part=[{"ID":1,"opcode":4},{"ID":2,"opcode":5},{"ID":3,"opcode":6}]
into this:
part_=[{"ID":1,"opcode":4},{"ID":2,"opcode":1},{"ID":2,"opcode":4},{"ID":3,"opcode":2},{"ID":3,"opcode":4}]
Currently my code is the following
def bit_set(theNumber,bit):
return theNumber&(1<<bit)!=0
part=[{"ID":1,"opcode":4},{"ID":2,"opcode":5},{"ID":3,"opcode":6}]
part_=[]
for i in part:
for j in range(32):
if bit_set(i["opcode"],j):
part_.append(i)
part_[-1]["opcode"]=(1<<j)
for i in part_:
print(i)
The output of the code is:
{'opcode': 4, 'ID': 1}
{'opcode': 1, 'ID': 2}
{'opcode': 2, 'ID': 3}
Interestingly if I modify the code slightly so that the value modification line is not there, the extra dictionaries are created, but obviously the opcode is not correct.
def bit_set(theNumber,bit):
return theNumber&(1<<bit)!=0
part=[{"ID":1,"opcode":4},{"ID":2,"opcode":5},{"ID":3,"opcode":6}]
part_=[]
for i in part:
for j in range(32):
if bit_set(i["opcode"],j):
part_.append(i)
#part_[-1]["opcode"]=(1<<j)
for i in part_:
print(i)
The output is
{'ID': 1, 'opcode': 4}
{'ID': 2, 'opcode': 5}
{'ID': 2, 'opcode': 5}
{'ID': 3, 'opcode': 6}
{'ID': 3, 'opcode': 6}
I can get around the issue by going about the problem a different way, but in the interest in learning what is going on I'm out of my depth.
This is caused as when you append i to the new list you do not create a copy of the dictionary instead you add a reference to the original dictionary. This means that when you change the dictionary in the next line you also change value in part. This causes the loop not to match the any more parts of the opcode. You can see this if you print out the values of part at the end of your code.
The python documentation explains this as:
Assignment statements in Python do not copy objects, they create bindings between a target and an object. For collections that are mutable or contain mutable items, a copy is sometimes needed so one can change one copy without changing the other.
Reference
You can fix this by creating a copy of the dictionary when you append it. This will allow you change the value without affecting the original dictionary. Python allows you to copy objects using the copy module (Documentation).
Just import copy and then do part_.append(copy.copy(i)) instead of part_.append(i).
import copy
def bit_set(theNumber,bit):
return theNumber&(1<<bit)!=0
part = [{"ID": 1, "opcode": 4}, {"ID": 2, "opcode": 5}, {"ID": 3, "opcode": 6}]
part_=[]
for i in part:
for j in range(32):
if bit_set(i["opcode"],j):
part_.append(copy.copy(i))
part_[-1]["opcode"]=(1<<j)
for i in part_:
print(i)

Resources