231

Say we have a list of numbers from 0 to 1000. Is there a pythonic/efficient way to produce a list of the first and every subsequent 10th item, i.e. [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?

nbro
  • 15,395
  • 32
  • 113
  • 196
p.brown
  • 2,403
  • 2
  • 17
  • 8

9 Answers9

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

Note that this is around 100 times faster than looping and checking a modulus for each element:

$ python -m timeit -s "lst = list(range(1000))" "lst1 = [x for x in lst if x % 10 == 0]"
1000 loops, best of 3: 525 usec per loop
$ python -m timeit -s "lst = list(range(1000))" "lst1 = lst[0::10]"
100000 loops, best of 3: 4.02 usec per loop
Mark Amery
  • 143,130
  • 81
  • 406
  • 459
Ned Deily
  • 83,389
  • 16
  • 128
  • 151
  • 6
    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 Sep 10 '09 at 06:45
  • 1
    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 Sep 10 '09 at 07:26
  • 12
    @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 Sep 10 '09 at 08:10
  • 11
    The `0` is redundant in `l[0::10]`. `l[::10]` is more readable, less confusing. – Konstantin Schubert Aug 05 '16 at 11:20
  • Im surprised by the performance comparison 0.5 seconds for list comprehension and 0.4s for list slice. Seems very slow, why list slicing needs 100 thousand loops for a list of size 1 thousand!? – Damo Jan 04 '18 at 11:51
  • 1
    Your comparison is not appropriate because you are selecting every element that is divisible by 10 instead of selecting every 10th element. It will be more appropriate to compare slicing notation with this: `lst = list(range(1000)); lst1 = [lst[i] for i in range(0, len(lst), 10)]`. On my machine, I get "1000000 loops, best of 5: 395 nsec per loop" for slicing notation and "20000 loops, best of 5: 11.1 usec per loop" for list comprehension. –  Oct 16 '20 at 14:05
  • this is correct answer, it returns every 10th element from the list indeed, and not element divisable by 10. (python 3.9). `my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]` `my_list[::10]` **Out[53]: [1, 11]** – Michal Kovacik Jun 20 '23 at 05:27
72
  1. source_list[::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 memory-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))
Mark Amery
  • 143,130
  • 81
  • 406
  • 459
Denis Otkidach
  • 32,032
  • 8
  • 79
  • 100
  • 1
    +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 Sep 10 '09 at 09:59
  • Iterable = object with __iter__() method returning iterator (object with next() method) – Denis Otkidach Sep 15 '09 at 13:01
35

Use range(start, end, step)

li = list(range(0, 1000, 10))

[0, 10, 20, 30, 40, 50, 60, 70, 80, 90 ... 990]

Or, if you have a list use slice: From manual: s[i:j:k] slice of s from i to j with step k

yourlist = [0, ... ,10 ...]  
sub = yourlist[::10]  # same as yourlist[0:100:10]

>>> sub
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
tuergeist
  • 9,171
  • 3
  • 37
  • 58
31

You can use the slice operator like this:

l = [1,2,3,4,5]
l2 = l[::2] # get subsequent 2nd item
Ionuț G. Stan
  • 176,118
  • 18
  • 189
  • 202
Nick Dandoulakis
  • 42,588
  • 16
  • 104
  • 136
13
newlist = oldlist[::10]

This picks out every 10th element of the list.

David Z
  • 128,184
  • 27
  • 255
  • 279
4

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
  • 74,297
  • 13
  • 168
  • 171
  • 3
    @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 Sep 10 '09 at 09:52
3
existing_list = range(0, 1001)
filtered_list = [i for i in existing_list if i % 10 == 0]
  • 1
    why do you have the if clause when range(0, 1001, 10) already only takes every 10th element? – Autoplectic Sep 10 '09 at 06:48
  • 4
    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 Sep 10 '09 at 07:20
  • 1
    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. –  Sep 10 '09 at 07:40
  • 1
    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 Sep 10 '09 at 11:14
1

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.

PaulMcG
  • 62,419
  • 16
  • 94
  • 130
-9

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
  • 4,428
  • 4
  • 24
  • 28
  • 1
    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. –  Sep 10 '09 at 06:41
  • 22
    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 Sep 10 '09 at 07:14