6
alphaValueDict = OrderedDict.fromkeys(string.ascii_uppercase,range(0)
i = 1
for k,v in alphaValueDict.iteritems():
    alphaValueDict[k] = [i]
    i += 1
return alphaValueDict

I need to create an ordered dict , where the keys are all the letters in the alphabet and the values are from 1 - 26. My question is, how can I use dict comprehension to do this in one line?

Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321

9 Answers9

15

You can avoid the dict-comprehension altogether:

>>> import string
>>> dict(zip(string.ascii_lowercase, range(1,27)))
{'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4, 'g': 7, 'f': 6, 'i': 9, 'h': 8, 'k': 11, 'j': 10, 'm': 13, 'l': 12, 'o': 15, 'n': 14, 'q': 17, 'p': 16, 's': 19, 'r': 18, 'u': 21, 't': 20, 'w': 23, 'v': 22, 'y': 25, 'x': 24, 'z': 26}

With an OrderedDict:

>>> import string
>>> from collections import OrderedDict
>>> OrderedDict(zip(string.ascii_lowercase, range(1,27)))
OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7), ('h', 8), ('i', 9), ('j', 10), ('k', 11), ('l', 12), ('m', 13), ('n', 14), ('o', 15), ('p', 16), ('q', 17), ('r', 18), ('s', 19), ('t', 20), ('u', 21), ('v', 22), ('w', 23), ('x', 24), ('y', 25), ('z', 26)])

I'd use a dict-comprehension only if you have to do some more computation to get the key/value or if it enhances readability (extreme example: {noun : age for noun, age in something()} gives you an idea of what we are talking about while dict(something()) does not).

Bakuriu
  • 98,325
  • 22
  • 197
  • 231
  • I will be repeatedly accessing the dict to get the value for each letter as I iterate over a list of of 5000 names, summing the total values of each name. – Padraic Cunningham Oct 23 '13 at 13:39
  • @PadraicCunningham In that case maybe using `ord` is simpler and more efficient. For each name do something like: `sum(map(ord, name)) - (ord('a') - 1) * len(name)`. – Bakuriu Oct 23 '13 at 13:51
  • @Bakurlu, it is for project Euler. I actually started off doing that before I realized that the values of the letters are specified as being from 1 to 26 in line with their position in the alphabet so ord would not work. unless I am misunderstanding you. – Padraic Cunningham Oct 23 '13 at 14:00
  • @PadraicCunningham You misunderstood my comment. `sum(map(ord, name))` will sum the ASCII value. If you subtract `(ord('a') - 1) * len(name)` you get the sum of the positions in the alphabet(since letters in ASCII are contiguous and in order). You could also write `sum(ord(letter) - ord('a') + 1 for letter in name)` where `ord(letter) - ord('a') + 1` is the position of `letter` in the alphabet. But since you subtract `ord('a')-1` from every element you can take it out of the summation (hence the `(ord('a') - 1) * len(name)`). – Bakuriu Oct 23 '13 at 14:54
  • sorry yes I was not sure what you meant, I saw quite a few people did it your way in the solution forum. – Padraic Cunningham Oct 23 '13 at 16:39
4

My take on it:

from string import ascii_uppercase
from collections import OrderedDict

od = OrderedDict((ch, idx) for idx, ch in enumerate(ascii_uppercase, 1))

Or:

from itertools import count, izip
od = OrderedDict(izip(ascii_uppercase, count(1)))
Jing Li
  • 14,547
  • 7
  • 57
  • 69
Jon Clements
  • 138,671
  • 33
  • 247
  • 280
3

Use enumerate() to count the letters:

>>> OrderedDict((k,v+1) for v,k in enumerate(string.ascii_uppercase))
OrderedDict([('A', 1), ('B', 2), ('C', 3), ('D', 4), ('E', 5), ('F', 6), ('G', 7), ('H', 8), ('I', 9), ('J', 10), ('K', 11), ('L', 12), ('M', 13), ('N', 14), ('O', 15), ('P', 16), ('Q', 17), ('R', 18), ('S', 19), ('T', 20), ('U', 21), ('V', 22), ('W', 23), ('X', 24), ('Y', 25), ('Z', 26)])

To answer the question at the end of the post, a dict comprehension is not an appropriate way to initialise an OrderedDict. Dict comprehensions only create ordinary dictionaries and if you use that to initialise your OrderedDict it is already too late, you have lost any ordering.

Duncan
  • 92,073
  • 11
  • 122
  • 156
3
theDict ={chr(y):y-64 for y in range(65,91)
print theDict

Output:

{'A':1,'B':2,......'X':24,'Y':25,'Z':26}
JonathanDavidArndt
  • 2,518
  • 13
  • 37
  • 49
Prem
  • 31
  • 1
2

The easiest way is to use zip and tuple unpacking

import string
{ k: v for k, v in zip(string.ascii_uppercase, xrange(1, len(string.ascii_uppercase)+1))}

Edited to take comments into account. It's a one liner, no dict comp. Just relies on OrderedDict taking an iterable and the power of zip.

OrderedDict(zip(string.ascii_uppercase, xrange(1, len(string.ascii_uppercase)+1)))
GoingTharn
  • 1,123
  • 1
  • 11
  • 19
2

Using the basic:

>>> from string import ascii_uppercase
>>> from collections import OrderedDict

One liner:

>>> OrderedDict((k, i+1) for i, k in enumerate(ascii_uppercase))

Yields:

OrderedDict([('A', 1), ('B', 2), ('C', 3), ('D', 4), ('E', 5), ('F', 6), ('G', 7), ('H', 8), ('I', 9), ('J', 10), ('K', 11), ('L', 12), ('M', 13), ('N', 14), ('O', 15), ('P', 16), ('Q', 17), ('R', 18), ('S', 19), ('T', 20), ('U', 21), ('V', 22), ('W', 23), ('X', 24), ('Y', 25), ('Z', 26)])
Inbar Rose
  • 41,843
  • 24
  • 85
  • 131
1
>>> import string
>>> string.ascii_lowercase
'abcdefghijklmnopqrstuvwxyz'
>>> s= string.ascii_lowercase

>>> from collections import OrderedDict
>>> odict = OrderedDict()
>>> for count,i in enumerate(s):
...     odict[count] = i
... 
>>> odict
OrderedDict([(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e'), (5, 'f'), (6, 'g'), (7, 'h'), (8, 'i'), (9, 'j'), (10, 'k'), (11, 'l'), (12, 'm'), (13, 'n'), (14, 'o'), (15, 'p'), (16, 'q'), (17, 'r'), (18, 's'), (19, 't'), (20, 'u'), (21, 'v'), (22, 'w'), (23, 'x'), (24, 'y'), (25, 'z')])
Foo Bar User
  • 2,401
  • 3
  • 20
  • 26
0
print {chr(ele+97): ele+1 for ele in xrange(26)}
Siva Cn
  • 929
  • 4
  • 10
0

Not using dictionary comprehension, a variation extending dict :

import string

class alph(dict):
    def __getitem__(self, key):
        if len(key) != 1:
            raise ValueError("Not a character.")
        return string.ascii_lowercase.index(key.lower()) + 1

Handles both upper and lower and throws ValueError for wrong keys.

Emil Davtyan
  • 13,808
  • 5
  • 44
  • 66