Splitting a list of strings into sublists where total of bytes of each sublist is <= N I'm given a list of strings and a length limit N. I have to write a function to accumulate consecutive strings in the list until the next string would exceed the N limit. I have to return a list of lists, where each list is the largest consecutive substring not to exceed N characters total. See the test case below for an example. Also, if any single string in the list is longer than N, I have to print a useful message and return.

My attempt to solve the problem has been this one:

def break_lst(lst, size):
    def len_lst(l):
        return len("".join(l))

    err = "Error: use a bigger size than " + str(size)
    result = []
    sublst = []

    for v in lst:
        sublst.append(v)
        ls = len_lst(sublst)

        if ls == size:
            result.append(sublst)
            sublst = []
        elif ls &gt; size:
            prev_sublst = sublst[:-1]
            if not prev_sublst or len_lst(prev_sublst) &gt; size:
                raise Exception(err)
            else:
                result.append(prev_sublst)
                sublst = [v]

    if sublst:
        if len_lst(sublst) &gt; size:
            raise Exception(err)
        else:
            result.append(sublst)

    return result

if __name__ == "__main__":
    lst = ["1", "22", "333", "4444", "55555", "666666", "7777777", "88888888"]

    for i in range(17):
        try:
            print(i, break_lst(lst, size=i))
        except Exception as e:
            print(e)


Output:



Error: use a bigger size than 0
Error: use a bigger size than 1
Error: use a bigger size than 2
Error: use a bigger size than 3
Error: use a bigger size than 4
Error: use a bigger size than 5
Error: use a bigger size than 6
Error: use a bigger size than 7
8 [['1', '22', '333'], ['4444'], ['55555'], ['666666'], ['7777777'], ['88888888']]
9 [['1', '22', '333'], ['4444', '55555'], ['666666'], ['7777777'], ['88888888']]
10 [['1', '22', '333', '4444'], ['55555'], ['666666'], ['7777777'], ['88888888']]
11 [['1', '22', '333', '4444'], ['55555', '666666'], ['7777777'], ['88888888']]
12 [['1', '22', '333', '4444'], ['55555', '666666'], ['7777777'], ['88888888']]
13 [['1', '22', '333', '4444'], ['55555', '666666'], ['7777777'], ['88888888']]
14 [['1', '22', '333', '4444'], ['55555', '666666'], ['7777777'], ['88888888']]
15 [['1', '22', '333', '4444', '55555'], ['666666', '7777777'], ['88888888']]
16 [['1', '22', '333', '4444', '55555'], ['666666', '7777777'], ['88888888']]


Problems with the function:


The proposed algorithm is really ugly and unpythonic
The fact it's handling an special case when the loop has finished makes it even uglier


How would you proceed in order to make a cleaner algorithm out of it?
You can greatly reduce the size of your code by using a total integer and one result list.  You can access the last item in the list by using result[-1].  Additionally, you can reduce the number of times you raise an error by just checking the size of each grouping.

def break_lst(lst, size):
    total = 0
    result = [[]]
    for v in lst:
        total += len(v)
        if total &lt;= size:
            result[-1].append(v)
        else:
            total = len(v)
            if total &gt; size:
                raise Exception("Error: use a bigger size than {}".format(size))
            result.append([v])
    return result