views:

457

answers:

9

Say we have a list of numbers from zero to 1000. Is there a pythonic/efficient way to produce a list of the first and every subsequent 10th item? ie. [0, 10, 20, 30 ...]

Yes I can do this using a for loop but I'm wondering if there is a neater way to do this, perhaps even in one line?

+30  A: 
>>> l = range(165)
>>> l[0::10]
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]

EDIT: just for fun, a little timing comparison (ignoring the boundary condition):

$ python -m timeit -s "l = range(1000)" "l1 = [x for x in l if x % 10 == 0]"
1000 loops, best of 3: 525 usec per loop
$ python -m timeit -s "l = range(1000)" "l1 = l[0::10]"
100000 loops, best of 3: 4.02 usec per loop
Ned Deily
List comprehensions are much readable and powerful than this solution.
Santi
[1, 11, 21, ...] != [0, 10, 20, ...]
Um, I thought I saw [1, 11, ] originally. For starting at 0, just change the slice operator: l[0::10].
Ned Deily
sorry I realized my mistake and edited it immediately. first item should be zero as I stated not 1. But now I'm confused; list comprensions are much more readable?
p.brown
@Santi - I'm not sure I'd agree. For basic "every Xth element" operations, I'd personally find Ned's solution more concise and easier to read.
Amber
Sure, list comprehensions are more powerful in general. OTOH, the question posits an existing list and, in that case, a slice works just fine.
Ned Deily
thanks for the explanation. you get the answer.
p.brown
@gs: Just curious, why did you feel it necessary to edit the answer to no longer match the problem as stated??
Ned Deily
@ Ned: To fit into the width of the code block. If he really needed a list from 0 to 1000 then he could have just used range(0, 1001, 10). Therefore I figured he meant it just as an example. Feel free to rollback my changes if you want.
Georg
I commented on this below in the list comprehension answers. Be careful with "if x % 10 == 0". It works only with this particular list example, but if the input list is for example l=range(0,1000,2) it won't pull out every 10th item.
Andre Miller
@Andre: very true. So this is an example of a humble language feature, the slice operator, which turns out in this case (1) to make it easier to get the correct results; (2) results in a more concise expression; and (3) happens to be 2 orders of magnitude faster. (1) is by far the most important concern, of course, but, thanks to the careful design and implementation of the language, you get all three for the price of 1. Nice question and responses.
Ned Deily
Alright, I got it now. I was terribly convinced that list comprehensions were "by design" the solution for this stuff that I couldn't see the simplicity in slicing.s/-1/+1
Santi
A: 
existing_list = range(0, 1001)
filtered_list = [i for i in existing_list if i % 10 == 0]
What's wrong with my answer? Why the downvote?
why do you have the if clause when range(0, 1001, 10) already only takes every 10th element?
Autoplectic
You are correct :)
Same comment here, this doesn't solve the more general problem of "Pythonic way to return list of every n’th item in a larger list" your solution depends on the fact that the example list's values are 0 to 1000 and only pulls items out of the list that has a value divisible by 10 instead of every 10th item.
Andre Miller
Well, the OP writes: "we have a list of numbers from zero to 1000". So he doesn't have the need for a general solution.
He writes 'Say we have..' which implies its just an example. If he really wanted every 10th number out of a list of zero to 1000 then the answer would be range(0,1001,10) or something similar.
Andre Miller
A: 

List comprehensions are exactly made for that:

smaller_list = [x for x in range(100001) if x % 10 == 0]

You can get more info about them in the python official documentation: http://docs.python.org/tutorial/datastructures.html#list-comprehensions

Santi
The upper bound should be 1000, not 10000. Your solution does not include the upper bound of 1000 since range stops at 999. +1 for the link to list-comprehension.
This doesn't actually pull out every 10th item, it pulls out every item that has a value divisible by 10. In this particular example its the same thing, but it might not be.
Andre Miller
+10  A: 

You can use the slice operator like this:

l = [1,2,3,4,5]
l2 = l[::2] # get subsequent 2nd item
Nick D
+4  A: 

From manual: s[i:j:k] slice of s from i to j with step k

li = range(100); sub = li[0::10]
>>> sub
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
tuergeist
Why the downvote?
tuergeist
+6  A: 
newlist = oldlist[::10]

This picks out every 10th element of the list.

David Zaslavsky
:-/ darn internet connection dropped out on me again...
David Zaslavsky
+1  A: 

Here is a better implementation of an "every 10th item" list comprehension, that does not use the list contents as part of the membership test:

>>> l = range(165)
>>> [ item for i,item in enumerate(l) if i%10==0 ]
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
>>> l = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
>>> [ item for i,item in enumerate(l) if i%10==0 ]
['A', 'K', 'U']

But this is still far slower than just using list slicing.

Paul McGuire
+3  A: 

Why not just use a step parameter of range function as well to get:

l = range(0, 1000, 10)

For comparison, on my machine:

H:\>python -m timeit -s "l = range(1000)" "l1 = [x for x in l if x % 10 == 0]"
10000 loops, best of 3: 90.8 usec per loop
H:\>python -m timeit -s "l = range(1000)" "l1 = l[0::10]"
1000000 loops, best of 3: 0.861 usec per loop
H:\>python -m timeit -s "l = range(0, 1000, 10)"
100000000 loops, best of 3: 0.0172 usec per loop
van
because OP already have a list he wants to slice
SilentGhost
@SilentGhost: That's true, but as this is a beginner question the range function might be what they *really* want to do, so I think it's a valid answer. (Though the upper limit should be 1001, not 1000)
Scott Griffiths
+6  A: 
  1. source_list[0::10] is the most obvious, but this doesn't work for any iterable and is not memory efficient for large lists.
  2. itertools.islice(source_sequence, 0, None, 10) works for any iterable and is memery efficient, but probably is not the fastest solution for large list and big step.
  3. (source_list[i] for i in xrange(0, len(source_list), 10))
Denis Otkidach
+1 Best answer, IMO. All three proposals a general solutions (ie. take the source list as a given). The generator solution (3.) is nice as it filters on the index of the source list. It's probably as memory efficient as 2. Both the indices and the result list are generators and thus constructed lazily, which is also probably the fastest if you don't need the result list in a single chunk. Only if the source list could be a generator I would go with Paul's "item, i in enumerate(l)" idiom, as there is no len() of a generator.BTW, which kind of iterable would not work with 1.? Generators?!
ThomasH
Iterable = object with __iter__() method returning iterator (object with next() method)
Denis Otkidach