143

So I want to create a list which is a sublist of some existing list.

For example,

L = [1, 2, 3, 4, 5, 6, 7], I want to create a sublist li such that li contains all the elements in L at odd positions.

While I can do it by

L = [1, 2, 3, 4, 5, 6, 7]
li = []
count = 0
for i in L:
    if count % 2 == 1:
        li.append(i)
    count += 1

But I want to know if there is another way to do the same efficiently and in fewer number of steps.

Frederik
  • 14,156
  • 10
  • 45
  • 53
veepsk
  • 1,703
  • 3
  • 14
  • 21
  • Does this answer your question? [Split a list into half by even and odd indexes?](https://stackoverflow.com/questions/11702414/split-a-list-into-half-by-even-and-odd-indexes) – Tomerikoo Nov 29 '20 at 08:59

5 Answers5

317

Solution

Yes, you can:

l = L[1::2]

And this is all. The result will contain the elements placed on the following positions (0-based, so first element is at position 0, second at 1 etc.):

1, 3, 5

so the result (actual numbers) will be:

2, 4, 6

Explanation

The [1::2] at the end is just a notation for list slicing. Usually it is in the following form:

some_list[start:stop:step]

If we omitted start, the default (0) would be used. So the first element (at position 0, because the indexes are 0-based) would be selected. In this case the second element will be selected.

Because the second element is omitted, the default is being used (the end of the list). So the list is being iterated from the second element to the end.

We also provided third argument (step) which is 2. Which means that one element will be selected, the next will be skipped, and so on...

So, to sum up, in this case [1::2] means:

  1. take the second element (which, by the way, is an odd element, if you judge from the index),
  2. skip one element (because we have step=2, so we are skipping one, as a contrary to step=1 which is default),
  3. take the next element,
  4. Repeat steps 2.-3. until the end of the list is reached,

EDIT: @PreetKukreti gave a link for another explanation on Python's list slicing notation. See here: Explain Python's slice notation

Extras - replacing counter with enumerate()

In your code, you explicitly create and increase the counter. In Python this is not necessary, as you can enumerate through some iterable using enumerate():

for count, i in enumerate(L):
    if count % 2 == 1:
        l.append(i)

The above serves exactly the same purpose as the code you were using:

count = 0
for i in L:
    if count % 2 == 1:
        l.append(i)
    count += 1

More on emulating for loops with counter in Python: Accessing the index in Python 'for' loops

Community
  • 1
  • 1
Tadeck
  • 132,510
  • 28
  • 152
  • 198
  • @TomWijsman look at [**this**](http://stackoverflow.com/questions/509211/good-primer-for-python-slice-notation) question for understanding python slicing syntax – Preet Kukreti Sep 15 '12 at 01:11
  • 1
    The question asks for odd positions. This gives even positions `(0,2,4,6)`; it sounds like OP wants indices `(1,3,5)`, which would be given by `[1,2,3,4,5,6,7][1::2]`. – Marcin Sep 15 '12 at 01:29
  • @Marcin: Yes, I actually came out with the same conclusion (see correction). This came out after I have read the OP's code carefully. The issue resulted on different base for indexing (for me "_odd_" element meant _first_ element, for OP seemingly it was _second_, so indexed at `1`). – Tadeck Sep 15 '12 at 01:33
  • 1
    @TomWijsman: I am sorry, I did not notice what you have changed. Indeed, there is a link, but it leads to Numarray project, not to Python's `list` slicing. It differs a little, especially because the `list`'s slice does not keep reference to the original list (in Numarray you need to explicitly call `.copy()` to have something not referencing original array). But it is nice to have something that may be better to some readers. Would you mind positing this link in the comment, so I can upvote it and it will appear just below the answer? – Tadeck Sep 15 '12 at 01:41
  • @Tadeck "Odd numbered indices" pretty naturally means indices which are odd numbers. – Marcin Sep 15 '12 at 02:16
  • @Marcin: I do not know where did you find that ("find on page" did not reveal that to me). I can see "_odd positions_", which pretty naturally can be interpreted both ways. Don't you think? – Tadeck Sep 15 '12 at 02:23
  • @Tadeck No, I don't think it's especially ambiguous. – Marcin Sep 15 '12 at 02:25
  • @Marcin: That is subjective. But I agree "odd numbered indices" would be a lot more clear. It is a pitty, it was not a part of the question. The code was part of the question and I did not go through it soon enough. Hope this will end our discussion on whether the first element can be interpreted as _odd_ :) Cheers! – Tadeck Sep 15 '12 at 02:33
  • Thank you so much. I was trying [0::2] because I was thinking "well zero is the 1st position, and 1 is odd, so I must need to start at zero". Wasn't thinking at all about the actual index count. You got my mind back on the right track. Upvote to you! – Longblog Dec 11 '14 at 17:47
14

For the odd positions, you probably want:

>>>> list_ = list(range(10))
>>>> print list_[1::2]
[1, 3, 5, 7, 9]
>>>>
dstromberg
  • 6,954
  • 1
  • 26
  • 27
8

I like List comprehensions because of their Math (Set) syntax. So how about this:

L = [1, 2, 3, 4, 5, 6, 7]
odd_numbers = [y for x,y in enumerate(L) if x%2 != 0]
even_numbers = [y for x,y in enumerate(L) if x%2 == 0]

Basically, if you enumerate over a list, you'll get the index x and the value y. What I'm doing here is putting the value y into the output list (even or odd) and using the index x to find out if that point is odd (x%2 != 0).

Sujay_K
  • 155
  • 1
  • 2
  • 10
peterb
  • 111
  • 1
  • 2
1

You can also use itertools.islice if you don't need to create a list but just want to iterate over the odd/even elements

import itertools
L = [1, 2, 3, 4, 5, 6, 7]
li = itertools.islice(l, 1, len(L), 2)
Péťa Poliak
  • 393
  • 1
  • 3
  • 11
0

You can make use of bitwise AND operator &:

>>> x = [1, 2, 3, 4, 5, 6, 7]
>>> y = [i for i in x if i&1]
[1, 3, 5, 7]

This will give you the odd elements in the list. Now to extract the elements at odd indices you just need to change the above a bit:

>>> x = [10, 20, 30, 40, 50, 60, 70]
>>> y = [j for i, j in enumerate(x) if i&1]
[20, 40, 60]

Explanation

Bitwise AND operator is used with 1, and the reason it works is because, odd number when written in binary must have its first digit as 1. Let's check:

23 = 1 * (2**4) + 0 * (2**3) + 1 * (2**2) + 1 * (2**1) + 1 * (2**0) = 10111
14 = 1 * (2**3) + 1 * (2**2) + 1 * (2**1) + 0 * (2**0) = 1110

AND operation with 1 will only return 1 (1 in binary will also have last digit 1), iff the value is odd.

Check the Python Bitwise Operator page for more.

P.S: You can tactically use this method if you want to select odd and even columns in a dataframe. Let's say x and y coordinates of facial key-points are given as columns x1, y1, x2, etc... To normalize the x and y coordinates with width and height values of each image you can simply perform:

for i in range(df.shape[1]):
    if i&1:
        df.iloc[:, i] /= heights
    else:
        df.iloc[:, i] /= widths

This is not exactly related to the question but for data scientists and computer vision engineers this method could be useful.

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Suvo
  • 1,318
  • 11
  • 13