1

I am pretty new to Python (3.6) and struggling to understand itertools groupby. I've got the following list containing integers:

    list1 = [1, 2, 0, 2, 3, 0, 4, 5, 0]

But the list could also be much longer and the '0' doesn't have to appear after every pair of numbers. It can also be after 3, 4 or more numbers. My goal is to split this list into sublists where the '0' is used as a delimiter and doesn't appear in any of these sublists.

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

A similar problem has been solved here already: Python spliting a list based on a delimiter word Answer 2 seemed to help me a lot but unfortunately it only gave me a TypeError.

    import itertools as it

    list1 = [1, 2, 0, 2, 3, 0, 4, 5, 0]

    list2 = [list(group) for key, group in it.groupby(list1, lambda x: x == 0) if not key]

    print(list2)

File "H:/Python/work/ps0001/example.py", line 13, in list2 = [list(group) for key, group in it.groupby(list, lambda x: x == '0') if not key]

TypeError: 'list' object is not callable

I would appreciate any help and be very happy to finally understand groupby.

Philipp
  • 323
  • 4
  • 19

6 Answers6

5

You were checking for "0" (str) but you only have 0 (int) in your list. Also, you were using list as a variable name for your first list, which is a keyword in Python.

from itertools import groupby

list1 = [1, 2, 0, 2, 7, 3, 0, 4, 5, 0]
list2 = [list(group) for key, group in groupby(list1, lambda x: x == 0) if not key]

print(list2)

This should give you:

[[1, 2], [2, 7, 3], [4, 5]]
Sid Sahay
  • 368
  • 5
  • 20
  • It gives me `TypeError: 'list' object is not callable` again. Could the expression `list(group)` be a problem? – Philipp Jan 09 '18 at 08:37
  • Did you change the name of your first list to something other than "list"? – Sid Sahay Jan 09 '18 at 08:39
  • yes, I changed it to list1 as in the OP. I tried your example by just copy and past and it still gave me that error. – Philipp Jan 09 '18 at 08:48
  • @schuph You'll have to change the reference of the list variable when you pass it to groupby as well. And, it seems to work for me: https://repl.it/@sahaysid/GroupByToSplitListOnDelim – Sid Sahay Jan 09 '18 at 08:52
  • What do you mean by changig reference of the list variable? Obviously your example works just fine but in my IPython console it gives me a TypeError referencing to this line `list2 = [list(group) for key, group in groupby(list1, lambda x: x == 0) if not key]`. – Philipp Jan 09 '18 at 09:08
  • By change the reference to the list variable in the call to groupby I meant change it from `list` to `list1` in your third line where you're assigning to `list2`. – Sid Sahay Jan 09 '18 at 09:13
  • So, your example is exactly what I needed. Thanks a lot for that! But it looks like Spyder/IPython doesn't like the expression `list(group)` because that must be the reason for my TypeError. I have just no idea why... – Philipp Jan 09 '18 at 10:03
  • That's really strange. Never encountered that problem before! – Sid Sahay Jan 09 '18 at 10:14
  • 1
    Restarting Spyder did the job! :D – Philipp Jan 09 '18 at 10:34
  • It probably had your definition of `list` still in memory. – Sid Sahay Jan 09 '18 at 12:05
2

In your code, you need to change lambda x: x == '0' to lambda x: x == 0, since your working with a list of int, not a list of str.

Since others have shown how to improve your solution with itertools.groupby, you can also do this task with no libraries:

>>> list1 = [1, 2, 0, 2, 3, 0, 4, 5, 0]
>>> zeroes = [-1] + [i for i, e in enumerate(list1) if e == 0]
>>> result = [list1[zeroes[i] + 1: zeroes[i + 1]] for i in range(len(zeroes) - 1)]
>>> print(result)
[[1, 2], [2, 3], [4, 5]]
RoadRunner
  • 25,803
  • 6
  • 42
  • 75
1

You can use regex for this:

>>> import ast 
>>> your_list = [1, 2, 0, 2, 3, 0, 4, 5, 0]
>>> a_list = str(your_list).replace(', 0,', '], [').replace(', 0]', ']')
>>> your_result = ast.literal_eval(a_list)
>>> your_result
([1, 2], [2, 3], [4, 5])
>>> your_result[0]
[1, 2]
>>> 

Or a single line solution:

ast.literal_eval(str(your_list).replace(', 0,', '], [').replace(', 0]', ']'))
hebeha
  • 362
  • 2
  • 17
  • Throwing Regex at everything is almost always a bad idea/design when there are native tools available. Regex has it's own ground. Also, if the list grows bigger, changing list to string, applying Regex, evaluating again will cost very badly. – heemayl Jan 09 '18 at 10:25
  • This is very Rube Goldberg and very brittle. For example, if the list contains a sublist, this will fail. – Aur Saraf Oct 13 '20 at 10:34
1

You could do that within a Loop as depicted in the commented Snippet below:

list1       = [1, 2, 0, 2, 3, 0, 4, 5, 0]
tmp,result  = ([],[])   # tmp HOLDS A TEMPORAL LIST :: result => RESULT

for i in list1:
    if not i:
        # CURRENT VALUE IS 0 SO WE BUILD THE SUB-LIST
        result.append(tmp)
        # RE-INITIALIZE THE tmp VARIABLE
        tmp = []
    else:
        # SINCE CURRENT VALUE IS NOT 0, WE POPULATE THE tmp LIST
        tmp.append(i)

print(result) # [[1, 2], [2, 3], [4, 5]]

Effectively:

list1       = [1, 2, 0, 2, 3, 0, 4, 5, 0]
tmp,result  = ([],[])   # HOLDS A TEMPORAL LIST

for i in list1:
    if not i:
        result.append(tmp); tmp = []
    else:
        tmp.append(i)

print(result) # [[1, 2], [2, 3], [4, 5]]
Poiz
  • 7,611
  • 2
  • 15
  • 17
0

Try to use join and then split by 0

lst = [1, 2, 0, 2, 3, 0, 4, 5, 0]

lst_string = "".join([str(x) for x in lst])
lst2 = lst_string.split('0')
lst3 = [list(y) for y in lst2]
lst4 = [list(map(int, z)) for z in lst3]
print(lst4)

Running on my console:

enter image description here

Frank AK
  • 1,705
  • 15
  • 28
0

Use zip to return a tuple of lists and convert them to list later on

>>> a
[1, 2, 0, 2, 3, 0, 4, 5, 0]
>>> a[0::3]
[1, 2, 4]
>>> a[1::3]
[2, 3, 5]
>>> zip(a[0::3],a[1::3])
[(1, 2), (2, 3), (4, 5)]
>>> [list(i) for i in zip(a[0::3],a[1::3])]
[[1, 2], [2, 3], [4, 5]]
Albin Paul
  • 3,330
  • 2
  • 14
  • 30