1

I have a list that looks like this -

nums = [0,0,0,0,1,1,2,3,4,5,6,0,0,0,0,1,2,3,4,5,6,0,0,0,0]

I want to get the numbers between the 0s in the list. For this, I used the code below -

groups = list(itertools.groupby(nums, lambda item:item != 0))
groups = list(filter(lambda item:item[0], groups))
list(map(lambda item:list(item[-1]), groups))

But I am getting empty lists as the output -

[[], []]

My desired output is -

[[1,1,2,3,4,5,6], [1,2,3,4,5,6]]

How can I do this using itertools.groupby?

Zero
  • 1,800
  • 1
  • 5
  • 16
  • See also: https://stackoverflow.com/questions/43454953/python-list-comprehension-not-working-for-itertools-groupby-decoding This is a common gotcha, so there *should* be an existing canonical duplicate, but I'm not able to find one right now. – Karl Knechtel May 28 '22 at 05:09
  • [Another](https://stackoverflow.com/q/48475888/12671057) – Kelly Bundy May 28 '22 at 05:33
  • @KarlKnechtel I knew that there would be a similar question that has already been asked, but was just not able to find it even after 30 minutes. – Zero May 28 '22 at 05:56
  • There's https://stackoverflow.com/questions/773/how-do-i-use-itertools-groupby, which could presumably solve the problem if you looked at the examples; but the title is misleading - it's written for OP's rather specific circumstance, and only accidentally avoids the gotcha by the choice of coding style. – Karl Knechtel May 28 '22 at 11:34

1 Answers1

2

Try:

from itertools import groupby

nums = [0,0,0,0,1,1,2,3,4,5,6,0,0,0,0,1,2,3,4,5,6,0,0,0,0]

output = [list(g) for k, g in groupby(nums, lambda x: x != 0) if k]
print(output) # [[1, 1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6]]

The doc puts

The returned group is itself an iterator that shares the underlying iterable with groupby(). Because the source is shared, when the groupby() object is advanced, the previous group is no longer visible.

Therefore my guest is that the first line

groups = list(itertools.groupby(nums, lambda item:item != 0))

is the culprit. As list iterates over groupby object, each group in it also gets consumed. You can instead use

groups = [(k, list(g)) for k, g in groupby(nums, lambda x: x != 0)]

to store the result.


To see this:

groups = groupby(nums, lambda x: x != 0)
k1, g1 = next(groups)
print(k1, list(g1)) # False [0, 0, 0, 0]

groups = groupby(nums, lambda x: x != 0)
k1, g1 = next(groups)
k2, g2 = next(groups)
print(k1, list(g1), k2, list(g2)) # False [] True [1, 1, 2, 3, 4, 5, 6]
j1-lee
  • 13,764
  • 3
  • 14
  • 26
  • It wasn't working because I converted the groupby output to a list?? – Zero May 28 '22 at 04:43
  • 1
    @Zero I guess so; [doc](https://docs.python.org/3/library/itertools.html#itertools.groupby) puts "The returned group is itself an iterator that shares the underlying iterable with `groupby()`. Because the source is shared, when the `groupby()` object is advanced, the previous group is no longer visible." – j1-lee May 28 '22 at 04:46
  • 1
    @Zero Thank you for the question; I didn't know about this behavior, and got puzzled after I first wrote my version of code. I learned from your question. – j1-lee May 28 '22 at 04:55