(more_)itertools function to group (predicate true/false) tuples with group borders at first item in group

72 Views Asked by At

I'm looking for an iterator preferably from itertools or more_itertools that groups my_list such that each group starts at the first even digit that follows after an odd digit (or has no predecessor). The single yielded groups shall also contain the odd digits trailing after 1 or multiple even digits.

is_even = lambda x: x % 2 == 0
my_list = [2, 8, 7, 6, 4, 2, 4, 5, 3, 4, 1, 2]
# is_even:[t, t, f, t, t, t, t, f, f, t, f, t] # t = true, f = false
# needles:                                


for group in magic_iterator(my_list, is_even):
    sub_list = list(group)
    # expected results:
    # first sub_list: [2,8,7]
    # second sub_list:[6,4,2,4,5,3]
    # third sub_list: [4,1]
    # fourth sub_list:[2]

I'm optimistic that there is a suitable iterator in more_itertools and I don't have to write it myself (see below) but I cannot find it.

I already tried partition but that splits my list into only two distinct sub groups. I also tried for _, group in groupby(my_list, key=is_even) with the usual groupby from itertools but it splits even and odd digits leading to ~twice as many groups as expected.

Code with similar result, but ugly

I came up with some dirty code that leads the expected result for comparison:

from itertools import groupby
from typing import List

result: List[List[int]] = []
last_group_has_been_added = False
for group_key, group in groupby(my_list, key=is_even):
    sub_list = list(group)
    if group_key is True:
        sub_group = sub_list
        last_group_has_been_added = False
    else:
        try:
            sub_group += sub_list
        except NameError:  # name 'sub_group' is not defined
            # occurs if the first item in my list is odd
            sub_group = sub_list
        result.append(sub_group)
        last_group_has_been_added = True
if not last_group_has_been_added:
    result.append(sub_list)

but that's not very pretty; I'm looking for a one-line solution ideally ;)

1

There are 1 best solutions below

0
On

The more_itertool you're looking for is split_when:

from more_itertools import split_when

is_even = lambda x: x % 2 == 0
my_list = [2, 8, 7, 6, 4, 2, 4, 5, 3, 4, 1, 2]

for group in split_when(my_list,lambda x,y: is_even(y) and not is_even(x)):
    sub_list = list(group)
    print(sub_list)

Prints as expected:

  • [2, 8, 7]
  • [6, 4, 2, 4, 5, 3]
  • [4, 1]
  • [2]

It also works if first item is odd.