Reputation: 412
Given a list [2,8,13,15,24,30], all the elements of which are supposed to be in range(31). Now I want to slice it into 3 lists, first list with numbers from 0 to 10, the second one with numbers from 11 to 20, and the others into the rest.
Here is my ugly code :
numbers = [2,8,13,15,24,30]
mylist = [[],[],[]] # I hate this the most...
for i in numbers:
if i <= 10 :
mylist[0].append(i)
elif i > 10 and i <= 20:
mylist[1].append(i)
else:
mylist[2].append(i)
print mylist
I don't think this is a good way to do it. Any suggestions please?
Upvotes: 5
Views: 1354
Reputation: 25974
Since your input is sorted you can do this in one pass with itertools.groupby
:
from itertools import groupby
[list(g) for _,g in groupby(numbers, lambda x: x//10)]
Out[3]: [[2, 8], [13, 15], [24, 26]]
No need to initialize a bunch of lists this way, groupby
yields them on the fly.
This may be off-by-one in terms of how you wanted to treat the modulo-10 boundaries; if it's unclear you can always define your own grouper
function:
def grouper(x):
'''bins matching the semantics:
[0,10] (10,20] (20, 30]'''
return (x-1)//10 if x > 0 else 0
and use it thusly:
numbers = [2,8,13,15,24,30]
[list(g) for _,g in groupby(numbers, grouper)]
Out[5]: [[2, 8], [13, 15], [24, 30]]
Upvotes: 3
Reputation: 21986
One nicer way to do this uses default dicts:
from collections import defaultdict
output = defaultdict(list)
for n in numbers:
output[n//10].append(n)
This creates a dict with a default element (if you access a key that hasn't been created) being an empty list. You don't need to create the empty list-of-lists you didn't like in your original code sample.
You can then access the output by decade, i.e. while output
is a dict, output[0]
is a list.
If you need to preserve your original output logic, converting this dict to a list-of-lists is simple.
Upvotes: 1
Reputation: 52030
What about using reduce
?
numbers = [2,8,13,15,24,26]
def part(acc, x):
# first list with numbers from 0 to 10,
# the second one with numbers from 11 to 20,
# and the others into the rest.
#
# This is *not* the same as:
# acc[x/10].append(x)
#
if x < 10:
acc[0].append(x)
elif x > 20:
acc[2].append(x)
else:
acc[1].append(x)
return acc
print reduce(part, numbers, [[],[],[]])
And to remove the hated [[],[],[]]
, if you can live with a dictionary instead of a list:
from collections import defaultdict
numbers = [2,8,13,15,24,26]
def part(acc, x):
if x < 10:
acc[0].append(x)
elif x > 20:
acc[2].append(x)
else:
acc[1].append(x)
return acc
print reduce(part, numbers, defaultdict(list))
Producing:
defaultdict(<type 'list'>, {0: [2, 8], 1: [13, 15], 2: [24, 26]})
Upvotes: 1
Reputation: 21986
Without rethinking your basic approach, you can do:
for n in numbers:
mylist[n//10].append(n)
This takes advantage of integer division, e.g. 19//10
= 1.
There are much more elegant ways to do this using other Python constructs; I will make a second answer for those. But for now this is a quick, easy and not too nauseating way to do what you want.
Upvotes: 1
Reputation: 211
Not sure what counts for elegant, but list comprehensions shoudl work:
numbers = [2,8,13,15,24,26]
mylist = []
mylist.append([x for x in numbers if x <=10])
mylist.append([x for x in numbers if x > 10 and x <= 20])
mylist.append([x for x in numbers if x > 2 ])
print mylist
Upvotes: 0
Reputation: 117941
def filterList(original, lower, upper):
return filter(lambda i : i > lower and i <= upper, original)
This can be called like so
firstSlice = filterList(numbers, 0, 10)
>>> firstSlice
[2, 8]
Making your range list
ranges = [0, 10, 20, 30]
Then making the 2D list in list comprehension
>>> [filterList(numbers, ranges[i], ranges[i+1]) for i in range(len(ranges)-1)]
[[2, 8], [13, 15], [24, 26]]
Upvotes: 1