Search code examples
pythonsortingcombinationspartition

What causes the output to be in incorrect order? Python


My goal is to take a one-dimensional list l as an argument and return all its partitions as a list. The function returns a three-dimensional Python list, where the lists contained in the second-level lists contain a total of the same elements as the entered list l without repetitions, and separately they contain all combinations of combinations like this.

Where I go wrong seems to be in the part where I'm required to sort the partitions.

This is my code:

from itertools import combinations

def makecomb(l):
    def partition(collection):
        if len(collection) == 1:
            yield [collection]
            return

        first = collection[0]
        for smaller in partition(collection[1:]):
            for n, subset in enumerate(smaller):
                yield smaller[:n] + [[first] + subset] + smaller[n + 1:]
            yield [[first]] + smaller

    result = list(partition(l))

    unique_result = [list(map(list, set(map(tuple, part)))) for part in result]

    def custom_sort(item):
        return (len(item), tuple(sorted(item[0], key=lambda x: l.index(x))))

    sorted_result = sorted(unique_result, key=custom_sort)

    return sorted_result

This is what is expected as an output when calling makecomb([1,2,3]):

[[[1], [2], [3]], [[1], [2, 3]], [[2], [1, 3]], [[3], [1, 2]], [[1, 2, 3]]]

Instead I get this:

[[[1, 2, 3]], [[1, 2], [3]], [[2], [1, 3]], [[2, 3], [1]], [[1], [2], [3]]]

I have now tried different approached, but nothing produces the correct output. At best I've managed to get:

[[[1], [2], [3]], [[1, 2], [3]], [[2], [1, 3]], [[2, 3], [1]], [[1, 2, 3]]]


Solution

  • I think you can get what you want with a double sort.

    from itertools import combinations
    
    def makecomb(l):
        def partition(collection):
            if len(collection) == 1:
                yield [collection]
                return
    
            first = collection[0]
            for smaller in partition(collection[1:]):
                for n, subset in enumerate(smaller):
                    yield smaller[:n] + [[first] + subset] + smaller[n + 1:]
                yield [[first]] + smaller
    
        result = list(partition(l))
        unique_result = [list(map(list, set(map(tuple, part)))) for part in result]
        for x in unique_result:
            x.sort(key=lambda item: (len(item), item))
        unique_result.sort(key=lambda item: (-len(item), item))
        return unique_result
    
    print(makecomb([1,2,3]))
    

    That should give you:

    [[[1], [2], [3]], [[1], [2, 3]], [[2], [1, 3]], [[3], [1, 2]], [[1, 2, 3]]]