Functioning of append in dictionary? - python

Please explain the use of append function in the following example:
Example 1:
new = {}
for (key, value) in data:
if key in new:
new[key].append( value )
else:
new[key] = [value]
and in example 2:
new = {}
for (key, value) in data:
group = new.setdefault(key, [])
group.append( value )

new is a dictionary, but the dictionary’s value are lists, which have the list.append() method.
In the loop, the dictionary is being filled with the values from data. Apparently, it is possible that there are multiple values for a single key. So in order to save that within a dictionary, the dictionary needs to be able to store multiple values for a key; so a list is being used which then holds all the values.
The two examples show two different ways of making sure that the dictionary value is a list when the key is new.
The first example explicitly checks if the key is already in the dictionary using key in new. If that’s the case, you can just append to the already existing list. Otherwise, you need to add a new list to the dictionary.
The second example uses dict.setdefault which sets a value if the key does not exist yet. So this is a special way of making sure that there’s always a list you can append the value to.

Related

Python: Obtaining index of an element within a value list of a dictionary

I have a dictionary with key:value list pairings, and I intend to find the index of the value list that contains the desired element.
E.g., if the dictionary is:
my_dict = {"key1":['v1'], "key2":None, "key3":['v2','v3'], "key4":['v4','v5','v6']}
Then, given element 'v2' I should be able to get index 2.
For a value list with one element, the index can be obtained with: list(my_dict.values()).index(['v1']) , however this approach does not work with lists containing multiple elements.
Using for loop, it can be obtained via:
for key, value in my_dict.items():
if value is None:
continue
if 'v2' in value:
print (list(my_dict.keys()).index(key))
Is there a neater (pythonic) way to obtain the same?
You've got an XY problem. You want to know the key that points to a value, and you think you need to find the enumeration index iterating the values so you can then use it to find the key by iteration as well. You don't need all that. Just find the key directly:
my_dict = {"key1":['v1'], "key2":None, "key3":['v2','v3'], "key4":['v4','v5','v6']}
value = 'v2'
# Iterate key/vals pairs in genexpr; if the vals contains value, yield the key,
# next stops immediately for the first key yielded so you don't iterate the whole dict
# when the value is found on an early key
key_for_value = next(key for key, vals in my_dict.items() if vals and value in vals)
print(key_for_value)
Try it online!
That'll raise StopIteration if the value doesn't exist, otherwise it directly retrieves the first key where the values list for that key contains the desired value.
If you don't really have an XY problem, and the index is important (it shouldn't be, that's a misuse of dicts) it's trivial to produce it as well, changing the extraction of the key to get both, e.g.:
index, key_for_value = next((i, key) for i, (key, vals) in enumerate(my_dict.items()) if vals and value in vals)
Mind you, this is a terrible solution if you need to perform these lookups a lot and my_dict isn't trivially small; it's O(n) on the total number of values, so a large dict would take quite a while to check (relative to the cost of just looking up an arbitrary key, which is average-case O(1)). In that case, ideally, if my_dict doesn't change much/at all, you'd construct a reversed dictionary up-front to find the key(s) associated with a value, e.g.:
from collections import defaultdict
my_dict = {"key1":['v1'], "key2":None, "key3":['v2','v3'], "key4":['v4','v5','v6']}
reversed_my_dict = defaultdict(set)
for key, vals in my_dict:
for val in vals:
reversed_my_dict[val].add(key)
reversed_my_dict = dict(reversed_my_dict) # Optional: Prevents future autovivification of keys
# by converting back to plain dict
after which you can cheaply determine the key(s) associated with a given value with:
reversed_my_dict.get(value, ()) # Using .get prevents autovivification of keys even if still a defaultdict
which returns the set of all keys that map to that value, if any, or the empty tuple if not (if you convert back to dict above, reversed_my_dict[value] would also work if you'd prefer to get a KeyError when the value is missing entirely; leaving it a defaultdict(set) would silently construct a new empty set, map it to the key and return it, which is fine if this happens rarely, but a problem if you test thousands of unmapped values and create a corresponding thousands of empty sets for no benefit, consuming memory wastefully).
Which you choose depends on how big my_dict is (for small my_dict, O(n) work doesn't really matter that much), how many times you need to search it (fewer searches mean less gain from reversed dict), and whether it's regularly modified. For that last point, if it's never modified, or rarely modified between lookups, rebuilding the reversed dict from scratch after each modification might be worth it for simplicity (assuming you perform many lookups per rebuild); if it's frequently modified, the reversed dict might still be worth it, you'd just have to update both the forward and reversed dicts rather than just one, e.g., expanding:
# New key
my_dict[newkey] = [newval1, newval2]
# Add value
my_dict[existingkey].append(newval)
# Delete value
my_dict[existingkey].remove(badval)
# Delete key
del my_dict[existingkey]
to:
# New key
newvals = my_dict[newkey] = [newval1, newval2]
for newval in newvals:
reversed_my_dict[newval].add(newkey) # reversed_my_dict.setdefault(newval, set()).add(newkey) if not defaultdict(set) anymore
# Add value
my_dict[existingkey].append(newval)
reversed_my_dict[newval].add(existingkey) # reversed_my_dict.setdefault(newval, set()).add(existingkey) if not defaultdict(set) anymore
# Delete value
my_dict[existingkey].remove(badval)
if badval not in my_dict[existingkey]: # Removed last copy; test only needed if one key can hold same value more than once
reversed_my_dict[badval].discard(existingkey)
# Optional delete badval from reverse mapping if last key removed:
if not reversed_my_dict[badval]:
del reversed_my_dict[badval]
# Delete key
# set() conversion not needed if my_dict's value lists guaranteed not to contain duplicates
for badval in set(my_dict.pop(existingkey)):
reversed_my_dict[badval].discard(existingkey)
# Optional delete badval from reverse mapping if last key removed:
if not reversed_my_dict[badval]:
del reversed_my_dict[badval]
respectively, roughly doubling the work incurred by modifications, in exchange for always getting O(1) lookups in either direction.
If you are looking for the key corresponding to a value, you can reverse the dictionary like so:
reverse_dict = {e: k for k, v in my_dict.items() if v for e in v}
Careful with duplicate values though. The last occurence will override the previous ones.
Don't know if it's the best solution but this works:
value = 'v2'
list(map(lambda x : value in x, list(map(lambda x : x[1] or [], list(my_dict.items()))))).index(True)

How to update a dictionary key that holds its values in a list?

I need to update a list based dictionary key without deleting the previous list items.
However, from solutions, I find on google they are updating the value gets replaced completely.
I want to still maintain the previous list items.
I have a python dictionary that stores projects phases with the completed percentage. I wish to update the completed percentage until the whole phase is 100% complete.
Here is my dictionary definition:
fiber_project = {"phase1":[10, 20,40,50], "phase2":[23, 39,90, 100],
"phase3":[30, 40,70, 100]}
This is how I am doing the update
#updating the phase2
fiber_project["phase2"] =[100]
Below is the output
Since fiber_project["phase2"] is a list, you can simply append to it:
fiber_project["phase2"].append(100)
This data structure is very useful, and python has a neat trick for constructing it, when you are adding an item to the value of some key, but do not already know if the key is in the dictionary or not. Naively, you would write (as I did initially):
if key not in d:
d[key] = []
d[key].append(val)
This code uses 3 lines, and 2 to 3 accesses to the dictionary by key. You could limit the number accesses to a hard 2, at the cost of an extra code line, using:
if key in d:
d[key].append(val)
else:
d[key] = [val]
Or, you can have a single line and single dictionary access by key, using the dict.setdefault method:
d.setdefault(key, []).append(val)

How to delete a key and its values from a dictionary based on its position Python

From what I have read, dictionaries are 'unordered' so finding a key and its values based on position is not possible. Is there another way to do this and return the same dictionary without the key and the key's values based on the position selected.
I want to set a variable to the position of the key so the user can select which key to delete along with that keys values (can be double ups of key names and values). The keys and values in this dictionary are strings, not integers and change everytime I run the script as the two strings I combine differ based on user input.
example to work off:
receipt = {'beef': 'mint', 'beef':'chips', 'chicken':'chips'}
How to remove a key by position
There is not a direct API for indexing dictionaries, but you can list the keys, select a key by position and then remove it from the dictionary.
def remove_key(d, i):
'Mutate the dictionary to remove i-th key'
keys = d.keys()
del d[keys[i]]
return d
Specific case for "double-ups"
If you data is doubled-up, a dictionary will eliminate the duplicates before you can get to the pairs. So, the data needs to be stored as a list of key/value pairs rather than as a dictionary. That will also allow you to directly delete by position:
>>> receipt = [('beef', 'mint'), ('beef', 'chips'), ('chicken', 'chips')]
>>> del receipt[1]
>>> receipt
[('beef', 'mint'), ('chicken', 'chips')]
What you can do is, sort the keys and use index of the key in that list to delete the key,value from the dict. See the below case. Improvise to use in your program.
mydict = {'c':'qwe','a':'asd','b':'acb'}
for key in sorted(mydict.keys()):
print(key,":",mydict[key])
This will print-
a:asd
b:acb
c:qwe
What you think is a dictionary: receipt = {'beef': 'mint', 'beef':'chips', 'chicken':'chips'} is not a dictionary, as dictionaries cannot have multiple values with the same key.
And as dicts cannot have multiple values with the same key, it doesn't make sense to remove values based on position.

How to update dictionary and return update key/value pairs

I am writing a function add_to_dict(d, key_value_pairs) which adds each given key/value pair to the given dictionary. The argument key_value_pairs will be a list of tuples in the form (key, value).
The function should return a list of all of the key/value pairs which have changed (with their original values).
def add_to_dict(d,key_value_pairs):
key_value_pairs=()
thelist=[]
thelist.append(list(d))
for key, value in key_value_pairs:
d[value]=key
thelist.append(list(key_value_pairs))
return thelist
What I got here seems completely not right and I have no clue at the moment.
From what I understand, you want to add a list of key/value tuples to a dictionary. The function will return all of the key/value pairs that were changed. I commented the problems I found in your code.
def add_to_dict(d,key_value_pairs):
key_value_pairs=() #This changes your list of key/value tuples into an empty tuple, which you probably don't want
thelist=[]
thelist.append(list(d)) #This appends all of the keys in d to thelist, but not the values
for key, value in key_value_pairs:
d[value]=key #You switched the keys and values
thelist.append(list(key_value_pairs)) #This is already a list, so list() is unnecessary
return thelist
I would suggest simply returning key_value_pairs as it already contains all of the keys and values that were modified. Let me know if you need more detail on how to fix the problems, but first try and figure it out yourself.

Removing one element from an entire dictionary

I've been working on this thing for hours, still cant figure it out :O
The problem I'm having is this. Lets say I have a dictionary with 4-element tuples as elemets and an integer as key. When an element is removed from the whole dictionary (which belongs to every tuple) making two of the tuples (elements) same, the keys of the two tuples don't add up. Instead, a new element is formed, with the key for that element being one of the previous 2 keys.
Let's say I have a dictionary:
dict={('A','B','D','C'): 4, ('C','B','A','D'):5, ('D','A','C','B'):3,('D','A','B','C'):1}
Now I wanna remove one letter from the entire dictionary.
for example, If I wanna remove 'B'. The following new dictionary is formed, but isn't returned, because two of the elements are the same.
{('A','D','C'): 4, ('C','A','D'):5, ('D','A','C'):3,('D','A','C'):1}
Instead of ('D','A','C'):3,('D','A','C'):1 becoming ('D','A','C'):4, this is what ends up happenening:
('D','A','C'):3 along with other tuples
So basically, one of the tuples disappears.
This is the method I'm currently using:
for next in dict:
new_tuple=()
for i in next:
if i!='A':
new_tuple+=(i,)
new_dict[new_tuple]=dict[next]
The above code returns new_dict as the following:
{('A','D','C'): 4, ('C','A','D'):5, ('D','A','C'):3}
So what can I do, to remove one letter from every tuple in the entire dictionary, and if two of the tuples look the same, they merge and the keys add up?
You will have to rebuild your entire dictionary, as each key/value pair is going to be affected. You can use a defaultdict to make the merging easier when you encounter now-overlapping keys:
from collections import defaultdict
new_dict = defaultdict(int)
for key, value in old_dict.items():
new_key = tuple(i for i in key if i != 'A')
new_dict[new_key] += value
Because when first looking up new_key in new_dict it'll be set to 0 by default, all we have to do is add the old value to update new_dict for when we first encounter a key. The next time we encounter the key the values are 'merged' by adding them up.

Categories

Resources