Jinx
Jinx

Reputation: 571

Python group list into subgroups with constraints

I really searched for this one, because I am almost certain some variation has been asked before but I couldn't put in the correct terms into Google to get a result that matches what I am trying to do. Generally seems like people are looking for the total combinations without constraints.

I am trying to do the following:

Given a list like this:

[1, 1, 2, 2, 3, 3] group it into as many groups of [1, 2, 3] as possible

So

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

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

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

[1, 4, 4, 7] -> [[1, 4, 7], [4]]

Notes:

  1. Input will always be sorted, but the values of these numbers is not known, so it will need to work in general sense.

  2. The idea is I have objects with certain attributes that need to be grouped together to create a different object, but sometimes I am given repeats (and potentially incomplete repeats) -- ie, I used to think that the attributes of my objects will always just be [1, 2, 3] but turns out sometimes I can get [1, 1, 2, 2, 3, 3] and I need a way to break that into two [1, 2, 3] lists to create an intermediate object downstream.

Upvotes: 3

Views: 463

Answers (1)

Corralien
Corralien

Reputation: 120409

You can use zip_longest and groupby from itertools:

from itertools import zip_longest, groupby

def f(l):
    z = zip_longest(*[list(g) for _, g in groupby(l)])
    return [[j for j in i if j is not None] for i in z]

Usage:

>>> f([1, 1, 2, 2, 3, 3])
[[1, 2, 3], [1, 2, 3]]

>>> f([1, 1, 2, 3, 3])
[[1, 2, 3], [1, 3]]

>>> f([1, 1, 3, 3, 5])
[[1, 3, 5], [1, 3]]

>>> f([1, 4, 4, 7])
[[1, 4, 7], [4]]

# Update
>>> f(sorted([1, 1, 2, 2, 3, 3, 1, 2]))
[[1, 2, 3], [1, 2, 3], [1, 2]]

# Update 2
>>> f([1, 1, 1, 2, 2, 2, 3, 3])
[[1, 2, 3], [1, 2, 3], [1, 2]]

Update

Alternative version suggested by @cards using filterfalse:

from itertools import zip_longest, groupby, filterfalse

def f(l):
    z = zip_longest(*[list(g) for _, g in groupby(l)])
    return [list(filterfalse(lambda j: j is None, i)) for i in z]

Upvotes: 5

Related Questions