Search code examples
pythonloopsgenerator

Python breaking down list in loop based on total


I have what I think is a fairly complicated problem to solve. For each item in a list I must get a total area. When the area of enough of those items meets a total area, those items must be split into a separate list. The process will then continue for the subsequent items in the list.

The code looks something like this, but does not work yet:

all_areas = [1,2,3,4,5,6,7,8,9]

def get_area(n):
  # in my actual code the function does more than just return n as it is.
  return n

for i in all_areas:
  total_area=0
  ids=[]
  while total_area < 20:
    area = get_area(i)
    total_area+=area
    ids.append(i)
  else:
    # pass along ids before resetting counters
    call_another_function(ids)
    total_area=0
    ids=[]

With 20 as the threshold and with the given parameters, I am intending the call_another_function will be called once with [1,2,3,4,5,6] and then another time with [7,8,9]. The loop will then finish. Some help with this problem would be appreciated.

I was also wondering if this would be a suitable circumstance to use yield and generators? These seem to be a useful capability of Python that I have not ever had to use before.


Solution

  • Generator solution:

    If you are looking to learn generators, here's a way to use them:

    def area_up_to(it, n):
        total_area = 0
        ids = []
        for x in it:
            total_area += get_area(x)
            ids.append(x)
            if total_area >= n:
                yield ids
                total_area = 0
                ids = []
        if ids:
            yield ids
    

    To use it, call the function to produce a generator:

    area_gen = area_up_to(all_areas, 20)
    

    Then you can iterate it or call next(area_gen) to get the next set:

    for x in area_gen:
        print(x)
    

    Output:

    [1, 2, 3, 4, 5, 6]
    [7, 8, 9]
    

    Fixing your code:

    You had some logic issues and a wild while..else in there, but this works:

    total_area=0
    ids=[]
    for i in all_areas:
        area = get_area(i)
        total_area += area
        ids.append(i)
        if total_area > 20:
            # pass along ids before resetting counters
            print(ids)
            total_area=0
            ids=[]
    if ids:
        print(ids)