creating a function for for-loop across specific set of ranges in python

Viewed 112

How can you loop across multiple different ranges. for example say i want to run a for loop for specific set of ranges

1 to 2, 4 to 6 and 10 to 13,

I don't know the exact final number so maybe a function might be useful.

The standard method of

for i in range(1,2) + range (4,6) + range (10,13)

I should have this

[1,4,5,6,10,11,13]

My question is, this is not so efficient, if i don't know the total number of ranges am covering, and I can't even create this as a function without so many parameters.

so if i want to have something like this

for i in range(a,b) + range (c,d) + range (e,f), ..... 

but as a simple function Can someone help with an efficient way of doing this ?

3 Answers

Simple, though most likely the least efficient solution is to use a nested for loop,

def multi_range_loop(ranges):

    all_values = []
    for rg in ranges:
        for i in range(rg[0], rg[1]):
            all_values.append(i)
    print(all_values)

my_ranges = [[1,3],[4,7],[10,14]]
multi_range_loop(my_ranges)

You can chain the ranges with itertools:

from itertools import chain

total_range = chain(range(1,2), range(4,6), range(10,13))

print(list(total_range))
#[1, 4, 5, 10, 11, 12]

Here is another solution with a function that returns a generator:

def total_range(lst):
    return (i for tup in lst for i in range(tup[0], tup[1]))

list_of_ranges = [(1,2), (4,6), (10,13)]
print(list(total_range(list_of_ranges)))

To have an iterable object from the list of iterable form the different ranges you can use the chain function as in the following response https://stackoverflow.com/a/67547449/1287983.

And in order to have dynamic ranges in a function you can do the following:

def get_ranges(list_ranges):
    lower_bounds, upper_bounds = zip(*list_ranges)
    return list(chain(*list(map(
        range, lower_bounds, upper_bounds))))

get_ranges([(1,2), (4,6), (10,13)])

[1, 4, 5, 10, 11, 12]

The function above returns the list of values. And if you want to iterate efficiently over the resulting values, you just need to return the iterator instead of the list. See the code below.

def get_ranges(list_ranges):
    lower_bounds, upper_bounds = zip(*list_ranges)
    return chain(*list(map(range, lower_bounds, upper_bounds)))

for val in get_ranges([(1,2), (4,6), (10,13), (2,5)]):
    print(val)

1 4 5 10 11 12 2 3 4

Related