Search code examples
pythonlist-comprehension

Creating a list comprehension that repeats certain values in a list for X times and then moves on to the next item


I have a very specific use-case for creating a list comprehension and I am having a bit of trouble figuring out how to do it. I am sure there must be a method or function that can help me, but I guess I am not aware of it.

Here is the scenario: Following code works as expected and generates all expected variations (8 in total)

List_A = ["apples","oranges"]
List_B = ["meat","chicken"]
flexibility = range(2)

list = []
for a in List_A:
    results = [(day, b, a) for day in flexibility for b in List_B]
    list.append(results)
print (list)

result

[[(0, 'meat', 'apples'), (0, 'chicken', 'apples'), (1, 'meat', 'apples'), (1, 'chicken', 'apples')], 
 [(0, 'meat', 'oranges'), (0, 'chicken', 'oranges'), (1, 'meat', 'oranges'), (1, 'chicken', 'oranges')]]

Here is the complication. Let's assume I have a rate limit of 5 calls per second, and the list above creates 8 elements, hence I would go above the rate limit. The way I thought I could overcome this, was to create another variable to pass on to the function that will use a different accounts to make my api request. In theory, each account would make only 5 requests. Thus in my example above, the first 5 elements of the list would have account 0 and the rest 3 would use account 1. I tried using list comprehension to achieve this, but the result is not what I expected:

no = [0,1,2]
List_A = ["apples","oranges"]
List_B = ["meat","chicken"]
flexibility = range(2)

list = []
for a in List_A:
    results = [(day, b, a,n) for day in flexibility for b in List_B for n in no]
    list.append(results)
print (list)

results

[[(0, 'meat', 'apples', 0), (0, 'meat', 'apples', 1), (0, 'meat', 'apples', 2), (0, 'chicken', 'apples', 0), (0, 'chicken', 'apples', 1), (0, 'chicken', 'apples', 2), (1, 'meat', 'apples', 0), (1, 'meat', 'apples', 1), (1, 'meat', 'apples', 2), (1, 'chicken', 'apples', 0), (1, 'chicken', 'apples', 1), (1, 'chicken', 'apples', 2)], 
 [(0, 'meat', 'oranges', 0), (0, 'meat', 'oranges', 1), (0, 'meat', 'oranges', 2), (0, 'chicken', 'oranges', 0), (0, 'chicken', 'oranges', 1), (0, 'chicken', 'oranges', 2), (1, 'meat', 'oranges', 0), (1, 'meat', 'oranges', 1), (1, 'meat', 'oranges', 2), (1, 'chicken', 'oranges', 0), (1, 'chicken', 'oranges', 1), (1, 'chicken', 'oranges', 2)]]

I get many more elements in my list than what I want. I still want the original 8 but the first five would use account 0 and the last 3 account 1.

The goal is to get this list:

[(0, 'apples', 'meat',0), 
 (0, 'apples', 'chicken',0), 
 (0, 'oranges', 'meat',0), 
 (0, 'oranges', 'chicken',0), 
 (1, 'apples', 'meat',0), 
 (1, 'apples', 'chicken',1), 
 (1, 'oranges', 'meat',1), 
 (1, 'oranges', 'chicken',1)]

How can I achieve this? thanks


Solution

  • A concise way to do this combines itertools.product to give the combinations, with enumerate to give you a running counter. You can floor-divide that counter by 5 to give your "account number":

    import itertools
    
    List_A = ["apples", "oranges"]
    List_B = ["meat", "chicken"]
    flexibility = range(2)
    rate_limit = 5
    
    res = [(n, a, b, i // rate_limit)
           for i, (n, a, b) in enumerate(itertools.product(flexibility, List_A, List_B))]
    print(res)
    

    which gives the output list you're looking for:

    [(0, 'apples', 'meat', 0), (0, 'apples', 'chicken', 0), (0, 'oranges', 'meat', 0), (0, 'oranges', 'chicken', 0), (1, 'apples', 'meat', 0), (1, 'apples', 'chicken', 1), (1, 'oranges', 'meat', 1), (1, 'oranges', 'chicken', 1)]