Reputation: 745
Let's say this is my list:
[5, 3, 13, 8, 1, 2, 6, 4, 10, 1, 12, 3, 7, 1, 4]
I want to break it into chunks respecting a maximum sum of 12 when possible so that the result should be like this:
[[5, 3], [13], [8, 1, 2], [6, 4], [10, 1], [12], [3, 7, 1], [4]]
Notice the code should not delete any integer greater than the maximum sum (in our case 13
) but keep it isolated in a sublist.
I managed to do that with this code:
lst = [5, 3, 13, 8, 1, 2, 6, 4, 10, 1, 12, 3, 7, 1, 4]
start = 0
stop = 1
max_chunk_sum = 12
lst_count = len(lst)
result = []
while not stop > lst_count:
chunk = lst[start:stop]
chunk_sum = sum(chunk)
chunk_count = len(chunk)
if chunk_sum < max_chunk_sum:
if stop != lst_count:
stop += 1
else:
result.append(lst[start:stop])
break
else:
if chunk_count == 1:
result.append(lst[start:stop])
start = stop
stop += 1
else:
stop -= 1
result.append(lst[start:stop])
start = stop
stop += 1
print(result)
Output:
[[5, 3], [13], [8, 1, 2], [6, 4], [10, 1], [12], [3, 7, 1], [4]]
But I feel there's a much smarter solution for this problem (maybe using list comprehension?).
Upvotes: 1
Views: 723
Reputation: 1570
This is a typical work for reduce...
from functools import reduce
lst = [5, 3, 13, 8, 1, 2, 6, 4, 10, 1, 12, 3, 7, 1, 4]
init = lst[0:1]
rest = lst[1:]
max_sum = 12
reduce(lambda acc, cur: [*acc[0:-1], [*acc[-1], cur]]
if sum(acc[-1]) + cur <= max_sum else
[*acc, [cur]],
rest,
[init])
PS: it's also a pure function (immutable)...
Upvotes: 1
Reputation: 42133
You could use accumulate to compute the running sums with breaks on chunk overruns. Then use these sums to determine the ranges of indexes to include in each group:
a = [5, 3, 13, 8, 1, 2, 6, 4, 10, 1, 12, 3, 7, 1, 4]
chunk = 12
from itertools import accumulate
sums = accumulate(a,lambda s,v: [s+v,v][s+v>chunk])
breaks = [ i+1 for (i,s),v in zip(enumerate(sums),a[1:]) if s+v>chunk]
groups = [ a[s:e] for s,e in zip([0]+breaks,breaks+[len(a)]) ]
print(groups)
# [[5, 3], [13], [8, 1, 2], [6, 4], [10, 1], [12], [3, 7, 1], [4]]
If you're looking for a solution without using libraries, here's a simple loop that will do it without using sum and without adding values multiple times :
array = [5, 3, 13, 8, 1, 2, 6, 4, 10, 1, 12, 3, 7, 1, 4]
chunk = 12
group,total = [],0
groups = [group]
for value in array:
total += value
if total > chunk:
group,total = [],value
groups.append(group)
group.append(value)
print(groups)
# [[5, 3], [13], [8, 1, 2], [6, 4], [10, 1], [12], [3, 7, 1], [4]]
Upvotes: 1
Reputation: 854
Similar to @phoenixo, but we can use a generator:
def cluster(iterable, max_int):
start, *iterable = iterable
cluster = [start]
for value in iterable:
if sum(cluster) + value <= max_int:
cluster.append(value)
else:
yield cluster
cluster = [value]
yield cluster
Which gives us:
In [204]: list(cluster(ints, 12))
Out[204]: [[5, 3], [13], [8, 1, 2], [6, 4], [10, 1], [12], [3, 7, 1], [4]]
Upvotes: 1
Reputation: 2123
Here I iterate over lst
, I use a sublist chunk
, I check if this chunk would be > 12, otherwise I append the new element i
in chunk
. If it would be > 12, then I append the chunk
to result
, and I create a new chunk.
lst = [5, 3, 13, 8, 1, 2, 6, 4, 10, 1, 12, 3, 7, 1, 4]
max_chunk_sum = 12
result = []
chunk = []
for i in lst:
if sum(chunk) + i <= max_chunk_sum:
chunk.append(i)
else:
if chunk:
result.append(chunk)
chunk = [i]
result.append(chunk)
print(result)
Output :
[[5, 3], [13], [8, 1, 2], [6, 4], [10, 1], [12], [3, 7, 1], [4]]
Upvotes: 2
Reputation: 43320
You could use a list comprehension, but you could just check if the sum of adding the next element to the previous list would break the maximum value, if it does, append a new list.
result = [[lst.pop(0)]]
for item in lst:
if sum(result[-1]) + item > max_chunk_sum:
result.append([item])
else:
result[-1].append(item)
Upvotes: 4