4

I have a Python list

num_list = list(range(1,33))

And need every other pair of numbers in the list, like this:

[1, 2, 5, 6, 9, 10 ... ]

I've figured out how to exclude certain indices from the list, like this

num_list[2::3]

> [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]

but haven't figured out how to allow it to capture two indices at a time.

semblable
  • 773
  • 1
  • 8
  • 26
  • 1
    Maybe: `[i for i in range(1,33) if i%4 != 0 and i%4 != 3]` if it's always a consecutive sequence from 1 to N. – jarmod Jan 07 '22 at 20:52

8 Answers8

9

You could use enumerate, then filter the index mathematically:

[v for i, v in enumerate(num_list) if i % 4 < 2]
luther
  • 5,195
  • 1
  • 14
  • 24
3

You can't do this with just one slice operation. You could do something like

[num_list[j] for i in range(0, len(num_list), 4) for j in (i, i+1)]

Edit: another option is this:

[x for i in range(0, len(num_list), 4) for x in num_list[i:i+2]]

and yet another (lazy) option is

def keep_two_skip_two(it):
    it = iter(it)
    try:
        while True:
            yield next(it)
            yield next(it)
            next(it)
            next(it)
    except StopIteration:
        return

list(keep_two_skip_two(num_list))
Dennis
  • 2,249
  • 6
  • 12
3

Another solution for your case would be a list comprehension combined with slicing and zip. Slicing only extracts the needed values, zips combines them together as pairs, and in the list comprehension, you can iterate over the pairs to form the final output. In code, that reads ,like

[p for pairs in zip(num_list[::4],num_list[1::4]) for p in pairs]
Simon Hawe
  • 3,968
  • 6
  • 14
3

You can just slice the list twice and then sort it.

sorted(num_list[0::4]+num_list[1::4])
semblable
  • 773
  • 1
  • 8
  • 26
3

You could make something a little bit more generic depending on what your pattern looks like. Instead of using an index based condition, you could apply a mask. e.g.

mask = [1, 1, 0, 0]

To create the mask where the values repeat such that it is the same length of your data, you can zip the data alongside itertools.cycle(m). Example:

import itertools


def filter_mask(data, mask):
    return [d for d, m in zip(data, itertools.cycle(mask)) if m]

Or use compress as @KellyBundy suggests!

def filter_mask(data, mask):
    return list(itertools.compress(data, itertools.cycle(mask)))

Example

>>> data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> filter_mask(data, mask=[1, 0])
[0, 2, 4, 6, 8, 10]
>>> filter_mask(data, mask=[0, 1])
[1, 3, 5, 7, 9]
>>> filter_mask(data, mask=[1, 1, 0, 0])
[0, 1, 4, 5, 8, 9]
>>> filter_mask(data, mask=[1, 0, 1, 1, 0])
[0, 2, 3, 5, 7, 8, 10]
flakes
  • 21,558
  • 8
  • 41
  • 88
2

Solution 1: Using two excludes:

del num_list[2::4]
del num_list[2::3]

Now your list is:

[1, 2, 5, 6, 9, 10, 13, 14, 17, 18, 21, 22, 25, 26, 29, 30]

Solution 2: Using compress and cycle from itertools:

list(compress(num_list, cycle([True, True, False, False]))))

Shorter version:

[*compress(num_list, cycle([1, 1, 0, 0]))]
Kelly Bundy
  • 23,480
  • 7
  • 29
  • 65
1

Using different range objects for two generators:

>>> [x for i in range(1,33,4) for x in range(i, i+2)]
[1, 2, 5, 6, 9, 10, 13, 14, 17, 18, 21, 22, 25, 26, 29, 30]

A lazy version that only generates the 2-element ranges on demand:

>>> from itertools import chain
>>> x = chain.from_iterable(range(i, i+2) for i in range(1,33,4))
>>> list(x)
[1, 2, 5, 6, 9, 10, 13, 14, 17, 18, 21, 22, 25, 26, 29, 30]
chepner
  • 497,756
  • 71
  • 530
  • 681
0

Here's a functional way to achieve this using itertools.chain.from_iterable along with zip on your sliced lists as:

>>> num_list = list(range(1,33))
>>> from itertools import chain

>>> list(chain.from_iterable(zip(num_list[::4], num_list[1::4])))
[1, 2, 5, 6, 9, 10, 13, 14, 17, 18, 21, 22, 25, 26, 29, 30]
Moinuddin Quadri
  • 46,825
  • 13
  • 96
  • 126