Reputation: 169
Suppose I want to create a dictionary that maps digits to numbers less than 100 ending in those digits as follows:
d = {}
for i in range(100):
r = i % 10
if r in d:
d[r] = d[r].append(i)
else:
d[r] = [i]
print d
First of all, when i is 20, d[r] is apparently a NoneType when I try to append to it, throwing an error. Why would this be? Secondly, I feel like my approach is inefficient, as the work in checking if r in d isn't propagated. Something like this would be better, I feel:
case(d[r]) of
SOME(L) => d[r] = L.append(i)
| NONE => d[r] = [i]
Is there a way to have that logic in python?
Upvotes: 0
Views: 62
Reputation: 61044
With Andrew's suggestion to use d[r].append(i)
, you get the desired answer:
In [3]: d
Out[3]:
{0: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90],
1: [1, 11, 21, 31, 41, 51, 61, 71, 81, 91],
2: [2, 12, 22, 32, 42, 52, 62, 72, 82, 92],
3: [3, 13, 23, 33, 43, 53, 63, 73, 83, 93],
4: [4, 14, 24, 34, 44, 54, 64, 74, 84, 94],
5: [5, 15, 25, 35, 45, 55, 65, 75, 85, 95],
6: [6, 16, 26, 36, 46, 56, 66, 76, 86, 96],
7: [7, 17, 27, 37, 47, 57, 67, 77, 87, 97],
8: [8, 18, 28, 38, 48, 58, 68, 78, 88, 98],
9: [9, 19, 29, 39, 49, 59, 69, 79, 89, 99]}
You could do this:
In [7]: for onesdigit in range(10):
...: d[onesdigit] = range(onesdigit, 100, 10)
Upvotes: 0
Reputation: 61519
First of all, when i is 20, d[r] is apparently a NoneType when I try to append to it, throwing an error. Why would this be?
This is because the following code is wrong:
d[r] = d[r].append(i)
.append
modifies the list as a side effect, and returns None
. So after the list is appended to, it gets thrown away and replaced with the None
value now being re-assigned into d[r]
.
Is there a way to have that logic in python?
There are a variety of hacks that can be used, but none of them are appropriate here.
Instead, solve the specific problem: "modify a dictionary value if present, or create a new value otherwise". This can be refined into "create an empty default value if absent, and then modify the value now guaranteed to be present".
You can do that using .setdefault
, or more elegantly, you can replace the dictionary with a collections.defaultdict
:
from collections import defaultdict
d = defaultdict(list)
for i in range(100):
r = i % 10
d[r].append(i)
Or you can solve the even more specific problem: "create a dictionary with a given pattern", i.e. from applying a rule or formula to an input sequence (in this case, the input is range(100)
:
from itertools import groupby
def last_digit(i): return i % 10
d = {k: list(v) for k, v in groupby(sorted(range(100), key=last_digit), last_digit)}
Or you can solve the even more specific problem, by taking advantage of the fact that range
takes another argument to specify a step size:
d = {i: range(i, 100, 10) for i in range(10)}
Upvotes: 1