Home > Software design >  Keep two, skip two in Python list
Keep two, skip two in Python list

Time:01-08

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.

CodePudding user response:

You could use enumerate, then filter the index mathematically:

[v for i, v in enumerate(num_list) if i % 4 < 2]

CodePudding user response:

Another solution that should work for you special case using list comprehension

[v for vals in zip(num_list[::4],num_list[1::4]) for v in vals]

CodePudding user response:

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))

CodePudding user response:

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]))]

CodePudding user response:

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]

CodePudding user response:

sorted(num_list[0::4] num_list[1::4])

CodePudding user response:

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]

CodePudding user response:

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]

CodePudding user response:

Here is one solution:

num_list = list(range(1,33))

i=0
j=0
new_list = []
for num in num_list:
    i =1
    if i == 3:
        i = 2
        j =1
        if j == 2:
            j=0
            i=0
        continue
    new_list.append(num)
print(new_list)

Output:

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

This will iterate through the list and skip 2 items after every 2 items.

It may be easier to read when expanded like this.

  •  Tags:  
  • Related