Dict Comprehension Error - python

I am trying to create a simple dictionary of each letter with a number afterward (from 1-26), like this: {'a': 1, 'b': 2, 'c': 3, ...}.
I wanted to try using a dictionary comprehension to do this, so I did:
from string import lowercase
d = {s:i for s in lowercase for i in range(1, 27)}
However, this results in: {'a': 26, 'b': 26, 'c': 26, ...}. I think this happens because it's iterating over every value in lowercase, assigning it to 1, then 2, then 3 (for every value) ending at 26. There are only 26 keys because since it's a dictionary, it won't have two keys of the same letter (so it overwrites all of them to 26 at the end). I am not sure how to fix this, so if I could get guidance on how to actually do this, that would be great.
I got it to work using dict() and zip(): dict(zip(lowercase, range(1, 27))). However, I want to know how to do this using a dictionary comprehension. Thanks!

With enumerate:
{s: i for i, s in enumerate(lowercase, 1)}

Related

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

Removing dictionaries from a list on the basis of duplicate value of key

I am new to Python. Suppose i have the following list of dictionaries:
mydictList= [{'a':1,'b':2,'c':3},{'a':2,'b':2,'c':4},{'a':2,'b':3,'c':4}]
From the above list, i want to remove dictionaries with same value of key b. So the resultant list should be:
mydictList = [{'a':1,'b':2,'c':3},{'a':2,'b':3,'c':4}]
You can create a new dictionary based on the value of b, iterating the mydictList backwards (since you want to retain the first value of b), and get only the values in the dictionary, like this
>>> {item['b'] : item for item in reversed(mydictList)}.values()
[{'a': 1, 'c': 3, 'b': 2}, {'a': 2, 'c': 4, 'b': 3}]
If you are using Python 3.x, you might want to use list function over the dictionary values, like this
>>> list({item['b'] : item for item in reversed(mydictList)}.values())
Note: This solution may not maintain the order of the dictionaries.
First, sort the list by b-values (Python's sorting algorithm is stable, so dictionaries with identical b values will retain their relative order).
from operator import itemgetter
tmp1 = sorted(mydictList, key=itemgetter('b'))
Next, use itertools.groupby to create subiterators that iterate over dictionaries with the same b value.
import itertools
tmp2 = itertools.groupby(tmp1, key=itemgetter('b))
Finally, create a new list that contains only the first element of each subiterator:
# Each x is a tuple (some-b-value, iterator-over-dicts-with-b-equal-some-b-value)
newdictList = [ next(x[1]) for x in tmp2 ]
Putting it all together:
from itertools import groupby
from operator import itemgetter
by_b = itemgetter('b')
newdictList = [ next(x[1]) for x in groupby(sorted(mydictList, key=by_b), key=by_b) ]
A very straight forward approach can go something like this:
mydictList= [{'a':1,'b':2,'c':3},{'a':2,'b':2,'c':4},{'a':2,'b':3,'c':4}]
b_set = set()
new_list = []
for d in mydictList:
if d['b'] not in b_set:
new_list.append(d)
b_set.add(d['b'])
Result:
>>> new_list
[{'a': 1, 'c': 3, 'b': 2}, {'a': 2, 'c': 4, 'b': 3}]

How to get the index with the key in a dictionary?

I have the key of a python dictionary and I want to get the corresponding index in the dictionary. Suppose I have the following dictionary,
d = { 'a': 10, 'b': 20, 'c': 30}
Is there a combination of python functions so that I can get the index value of 1, given the key value 'b'?
d.??('b')
I know it can be achieved with a loop or lambda (with a loop embedded). Just thought there should be a more straightforward way.
Use OrderedDicts: http://docs.python.org/2/library/collections.html#collections.OrderedDict
>>> x = OrderedDict((("a", "1"), ("c", '3'), ("b", "2")))
>>> x["d"] = 4
>>> x.keys().index("d")
3
>>> x.keys().index("c")
1
For those using Python 3
>>> list(x.keys()).index("c")
1
Dictionaries in python (<3.6) have no order. You could use a list of tuples as your data structure instead.
d = { 'a': 10, 'b': 20, 'c': 30}
newd = [('a',10), ('b',20), ('c',30)]
Then this code could be used to find the locations of keys with a specific value
locations = [i for i, t in enumerate(newd) if t[0]=='b']
>>> [1]
You can simply send the dictionary to list and then you can select the index of the item you are looking for.
DictTest = {
'4000':{},
'4001':{},
'4002':{},
'4003':{},
'5000':{},
}
print(list(DictTest).index('4000'))
No, there is no straightforward way because Python dictionaries do not have a set ordering.
From the documentation:
Keys and values are listed in an arbitrary order which is non-random, varies across Python implementations, and depends on the dictionary’s history of insertions and deletions.
In other words, the 'index' of b depends entirely on what was inserted into and deleted from the mapping before:
>>> map={}
>>> map['b']=1
>>> map
{'b': 1}
>>> map['a']=1
>>> map
{'a': 1, 'b': 1}
>>> map['c']=1
>>> map
{'a': 1, 'c': 1, 'b': 1}
As of Python 2.7, you could use the collections.OrderedDict() type instead, if insertion order is important to your application.
#Creating dictionary
animals = {"Cat" : "Pat", "Dog" : "Pat", "Tiger" : "Wild"}
#Convert dictionary to list (array)
keys = list(animals)
#Printing 1st dictionary key by index
print(keys[0])
#Done :)

How can I report an error if a dictionary contains duplicates?

My code is something like:
d = defaultdict(list)
for prod_no ,production in enumerate(productions):
cp = Production(*productions[prod_no])
count_yields = len(cp.pattern_list())
#temp.setdefault(temp[cp.lhs()], []).append(count_yields)
d[cp.lhs()].append(count_yields)
print d
As an output I am getting something like given below:
defaultdict(<type 'list'>, {'A': [3, 3, 4, 3], 'S': [1], 'B': [4,5]})
Now I need to report an error because key 'A' has different multiple values like 3 and 4. Same can be said about key 'B'.
There should not be any error if I am getting an output like
defaultdict(<type 'list'>, {'A': [3, 3, 3, 3], 'S': [1]})
because both 'A' and 'S' has same values throughout...
You should use sets instead of lists as the value of your dictionary if you don't want duplicate values. In any case, you can check for duplicate values with
dct = {'A': [4,3,3,3], 'S': [1]}
if any(len(v) != len(set(v)) for v in dct.values()):
raise ValueError('Duplicate values in an item list')
If you want to check for duplicates in a list(acc. to the title), you could convert it into a set and check for its length (in this case, 1):
if not all(len(set(items))==1 for items in d.values()):
#A list against some key does not have all-same elements.
if d is the dictionary (it can be any dictionary including the default dictionary). You can use the following code. This will check for repetitions in the dictionary
for i in d.values():
if len(set(i)) != len(i):
print str(i) + " error"
I can't quite make out the Python syntax in your example, so I'll answer you based on two different ideas.
What you want to do depends on when you want to do it. For example, if you want to prevent a duplicate value, then here is one way to do that:
x_set = {'A':[4, 3]}
incoming_val = 3
if incoming_val in x_set['A']:
print("The incoming value already present.")
If you want to eliminated duplicate values after error reporting, you could do the following:
>>> x_set
{'A': [4, 3, 3]}
>>> list(set(x_set['A']))
[3, 4]
>>>
You could also put the list append attempt in a try .. catch and signal an exception and catch it. That is also a Pythonic thing to do.

Getting rid of parentheses after dictionary intersection

There is one question I am working on and got a very close answer... basically, the question is that you get two dictionaries and you have to find elements that intersect from both dictionaries and then create those elements (one same key from both dicts and two values from both dics) in a new dictionary.
a = {'A':17,'B':31,'C':42,'D':7,'E':46,'F':39,'G':9}
b = {'D':8,'E':3,'F':2,'g':5}
def intersect(a,b):
c = set(a).intersection(set(b))
d = {}
for i in c:
if i in a:
d[i] = int(a[i]),int(b[i])
return d
OUTPUT: {'E': (46, 3), 'D': (7, 8), 'F': (39, 2)}
I want to get the output like {'E': 46, 3, 'D': 7, 8, 'F': 39, 2}
How do I get rid of the parentheses around the values?
The code as you have written won't output anything at all. However, if you want to remove parentheses, then you can use this.
str(intersect(a, b)).replace('(', '').replace(')', '')
or equivalently this, which is a bit more concise and efficient
str(intersect(a, b)).translate(None, '()')
The output you are seeing is the python representation of your dictionary. What you have built (and, as far as I can tell, you have built it correctly -- it's what you want) is a dictionary mapping keys to pairs of items. The pairs are tuples, and those get printed with parentheses around them.
It sounds like what you want is an method which takes your dictionary and prints it, formatted in a specific way.
Something like this would print the dictionary the way you want it to:
def dictionary_printer(d):
print "{%s}" % ', '.join(
[("'%s': %s" % (key, ', '.join(map(str,value))))
for key, value in d.items()]
)

Categories