skipping a certain range of a list at time in python

Viewed 158

I have a array, I want to pick first 2 or range, skip the next 2, pick the next 2 and continue this until the end of the list

list = [2, 4, 6, 7, 9,10, 13, 11, 12,2]
results_wanted = [2,4,9,10,12,2] # note how it skipping 2. 2 is used here as and example

Is there way to achieve this in python?

5 Answers
from itertools import compress, cycle

results = list(compress(lst, cycle([1,1,0,0])))

or

results = [x for i, x in enumerate(lst) if i % 4 < 2]

Or if you don't need the original list anymore, so that it's ok to modify it instead of building a new one (if you do want a new one, you can still use this on a copy):

del lst[2::4], lst[2::3]

Benchmark with a list of a million elements:

  8.6 ms ± 0.1 ms _del_slices
 11.6 ms ± 0.1 ms _compress_bools
 13.6 ms ± 0.0 ms _compress_ints
 14.1 ms ± 0.0 ms _copy_del_slices
 22.6 ms ± 0.2 ms _copy_slices
 46.0 ms ± 0.2 ms Black_Raven
 59.5 ms ± 0.4 ms Vikrant_Sharma
 84.8 ms ± 0.1 ms _enumerate_modulo
161.8 ms ± 0.6 ms RCvaram

Not including the numpy solution because I'd have to switch to an older Python version for that and because it's not meaningful to compare (np.array(lst) already takes ~55 ms).

Benchmark code (Try it online!):

def _compress_ints(lst):
    return list(compress(lst, cycle([1,1,0,0])))

def _compress_bools(lst):
    return list(compress(
        lst,
        cycle(chain(repeat(True, 2),
                    repeat(False, 2)))
    ))

def _enumerate_modulo(lst):
    return [x for i, x in enumerate(lst) if i % 4 < 2]

def _del_slices(lst):
    del lst[2::4], lst[2::3]
    return lst

def _copy_del_slices(lst):
    results = lst[:]
    del results[2::4], results[2::3]
    return results

def _copy_slices(lst):
    a = lst[::4]
    b = lst[1::4]
    results = [None] * (len(a) + len(b))
    results[::2] = a
    results[1::2] = b
    return results
    
def Black_Raven(list1):
    add = skip = 2
    list2 = []
    for i in range(0, len(list1), skip+add):
        list2 += list1[i:i+add]
    return list2

def Vikrant_Sharma(l):
    n = 2
    return [x for i in range(0, len(l), n + n) for x in l[i: i + n]]

def RCvaram(test):
        skip = 2
        desireList = []
        skipMode = False
        for i in range(0,len(test)):
            if skipMode==False:
                desireList.append(test[i])
            if (i+1)%skip==0:
                skipMode=not skipMode
        return desireList

funcs = [_compress_ints, _compress_bools, _enumerate_modulo, _del_slices, _copy_del_slices, _copy_slices, Black_Raven, Vikrant_Sharma, RCvaram]

from timeit import default_timer as timer
from itertools import compress, cycle, repeat, chain, islice
from random import shuffle
from statistics import mean, stdev
import gc

# Correctness
lst = [2, 4, 6, 7, 9,10, 13, 11, 12,2]
results_wanted = [2,4,9,10,12,2]
for func in funcs:
    assert func(lst[:]) == results_wanted
for n in range(100):
    lst = list(range(n))
    expect = funcs[0](lst[:])
    for func in funcs:
        assert func(lst[:]) == expect, func.__name__

# Speed
times = {func: [] for func in funcs}
def stats(func):
    ts = [t * 1e3 for t in sorted(times[func])[:3]]
    return f'{mean(ts):5.1f} ms ± {stdev(ts):.1f} ms'
original = list(range(1000000))
for _ in range(15):
    shuffle(funcs)
    for func in funcs:
        lst = original.copy()
        gc.collect()
        t0 = timer()
        result = func(lst)
        t = timer() - t0
        del result
        times[func].append(t)
for func in sorted(funcs, key=stats):
    print(stats(func), func.__name__)

Taking n number of elements and skipping the next n.

l = [2, 4, 6, 7, 9, 10, 13, 11, 12, 2]
n = 2
wanted = [x for i in range(0, len(l), n + n) for x in l[i: i + n]]
### Output : [2, 4, 9, 10, 12, 2]

I didn't use any python prebuild techniques. I used traditional for loop with if-else conditions

  1. We have to skip based on a particular number.
  2. This skip needs to be done based on boolean parameter which I defined as skipMode
  3. if skipMode is true then the digits will not be added to the list 4 this skipMode will be changed based on the skipNumber
test = [2, 4, 6, 7, 9,10, 13, 11, 12,2]

def skipElementsByPositions(test,skip):
    if(skip> len(test)):
        return -1
    else:
        desireList = []
        skipMode = False
        for i in range(0,len(test)):
            if skipMode==False:
                desireList.append(test[i])
            if (i+1)%skip==0:
                skipMode=not skipMode
        return desireList

print(skipElementsByPositions(test,2)) #2,4,9,10,12,2
print(skipElementsByPositions(test,3)) #2, 4, 6, 13, 11, 12

You can try to iterate using range(start, end, skip)

And you can specify how many to add (add=2) and how many to skip (skip=2) in the sequence

list1 = [2, 4, 6, 7, 9,10, 13, 11, 12,2, 4, 6, 7, 9,10, 13, 11, 12,2]
list2 = []
add = 2
skip = 2

for i in range(0, len(list1), skip+add):
    list2 += list1[i:i+add]

print(list2)

Output:

[2, 4, 9, 10, 12, 2, 7, 9, 11, 12]

By the way you should avoid using the Python reserved word list as a variable name.

As you have a tag, here are numpy approaches.

Using a mask:

lst = [2, 4, 6, 7, 9,10, 13, 11, 12,2]
a = np.array(lst)

mask = [True, True, False, False]
n = (len(a)+3)//4
a[np.tile(mask, n)[:len(a)]]

Or with intermediate reshaping into a 2D array:

n = (len(a)+3)//4
extra = n*4-len(a)
(np
  .pad(a, (0, extra), mode='constant', constant_values=0)
  .reshape(-1,4)
  [:,:2]
  .ravel()
  )

Output: array([ 2, 4, 9, 10, 12, 2])

Related