How to use Python to filter list based on the next requirements?

Viewed 179

List contains only integers. I need to select elements with the next requirements (every requirement is a separate problem):

  1. Elements between 1 and 1: [2,1,3,1,3] -> [3], [2,1,3,4,1,3] -> [3,4]

  2. Elements between same numbers: a) only one pair of same number is allowed: [1,2,3,2] -> [3] (2 and 2), [1,4,3,5,4] -> [3,5], (4 and 4) b) multiple pairs is allowed: [1,2,1,2] -> [[2],[1]], (pairs of: (1,1),(2,2)); [1,4,3,5,4,3,2] -> [[3,5],[5,4], (pairs of: (4,4), (3,3))

  3. Elements that have more than one same neighborhood: [1,1,3] -> [1,1], (2 consecutive 1s), [1,1,3,2,2,2,1] -> [1,1,2,2,2] (2 consecutive 1s and 3 consecutive 2s)

What is general approach to this problem? I have worked with filter but only with one parameter predicate: filter(lambda x: (x%2 == 0), numbers)

Is there another approach rather than nested for loops? Maybe more functional style. Is it possible to use convolutions as a solution?

4 Answers

For Q1, you can create a list with the indices of 1s and iterate over the list again to find the items between ones.

ones = [i for i,x in enumerate(lst) if x==1]
for i,j in zip(ones, ones[1:]):
    print(lst[i+1:j])

Output:

[2,1,3,1,3]   -> [3]
[2,1,3,4,1,3] -> [3, 4]

For Q2, similar to Q1, iterate over the list to find the indices of items and keep them in a dictionary. Then iterate over it to find the numbers that occur more than once and print out the items between these numbers:

d = {}
for i,x in enumerate(lst):
    d.setdefault(x, []).append(i)
for k,indices in d.items():
    if len(indices)>1:
        for i,j in zip(indices, indices[1:]):
            print(lst[i+1:j])

Output:

[1,2,3,2]   -> [3]
[1,4,3,5,4] -> [3, 5]

Here is a solution for Q1/Q3 using itertools and of Q2 using a classical loop:

Q1

This one groups by equality to 1, this then drops the first and last group (either 1 or not 1 but in any case we don't want to keep), then drops the groups equal to 1 leaving only the inner groups is any.

l1 = [2,1,3,4,1,3,6,1,0]

from itertools import groupby, chain

list(chain.from_iterable(g for k,g in
                         [(k,list(g)) for k,g in
                          groupby(l1, lambda x: x==1)][1:-1]
                         if not k))
# [3, 4, 3, 6]
Q3

Here we group by identical consecutive values and filter using the group length if greater than 1.

NB. there will be side effects if a value is present more than 2 times, in which case the expected behavior should be explicited

l3 = [1,1,3,2,2,2,1]

from itertools import groupby, chain

list(chain.from_iterable(l for k,g in groupby(l3) if len(l:=list(g))>1))
# [1, 1, 2, 2, 2]
Q2

For this one, we first read the list to identify duplicated values. Then we read the list again and add the value to a dictionary of lists with duplicates as key after the key was encountered once.

l2 = [1,4,3,5,4,3,2]

from collections import Counter

dups = {k: [] for k,v in Counter(l2).items() if v>1}
active = set()

for i in l2:
    if i in dups: # if many keys are expected initialize and use a set of the keys to improve efficiency
        active.remove(i) if i in active else active.add(i)
    for k in active:
        if i != k:
            dups[k].append(i)

list(dups.values())
# [[3, 5], [5, 4]]

Part 3 can be solved without nested for loops using itertools.groupby

from itertools import groupby
def repeating_sections(lst):
    out_lst = []
    for key, group in groupby(lst):
        _lst = list(group)
        if len(_lst) > 1:
            out_lst += _lst
    return out_lst
print(repeating_sections([1, 1, 3, 2, 2, 2, 1]) 
# [1, 1, 2, 2, 2]

Maybe you can create a function? It meets all three criteria

def filterNumbers(numList, targetNum) -> list:
firstPosition = numList.index(targetNum)
lastPosition = numList.index(targetNum, firstPosition + 2)
while True:
    try:
        if lastPosition != (len(numList)-1):
            if numList[lastPosition] == numList[lastPosition+1]:
                lastPosition = lastPosition + 1
                continue
        break
    except Exception as e:
        print(e)
resultList = numList[firstPosition+1:lastPosition]
return resultList

Condition 1:

test1 = [2, 1, 3, 1, 3]
filterNumbers(test1, 1)

#Output:
[3]

Condition 2:

test2 = [1, 4, 3, 5, 4]
filterNumbers(test2, 4)

#Output:
[3, 5]

Condition 3:

test3 = [1,1,3,2,2,2,1]
filterNumbers(test3, 1)

#Output
[1, 3, 2, 2, 2]

I am assuming there is a typo while you were writing the third condition because you skipped the 3 and included the first 1.

Related