Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generate list of range tuples with given boundaries in python

[edit] I'm not sure if this better off to codereview, if so, please migrate :) thanks!

So we were sitting here, working on a semi-academic problem.

Given a start, stop and step, generate a list of range tuples, so that

gen_range(100, 140, 10)

would produce

[(100, 110), (110, 120), (120, 130), (130, 140)]

also, given then fact that it should work on, say, iterating 500M integers with a step of 100 and not take forever.

The implementation I've come up with is as follows:

def gen_range(start, stop, step):
    llist = range(start, stop+step, step)
    batch_list = []

    if llist[-1] > stop:
        llist[-1] = stop

    for a, b in enumerate(llist[:-1]):
        batch_list.append((llist[a], llist[a+1]))

    print batch_list

gen_range(100000000,600000000,100)

But I can't stop thinking that it can be done in a more efficient (also code-length wise) way. Any suggestions?

[edit]

One thing I forgot to point out. If the range boundary is not equal to the step, i.e. you have a case of:

gen_range(100, 143, 10)

the upper boundary should be 143, and not 150, as some answers here would produce, due to range() internals.

like image 947
favoretti Avatar asked Dec 27 '12 02:12

favoretti


4 Answers

Maybe code-length wise if (b - a) % c == 0:

def gen_range(a, b, c):
    return [(i, i + c) for i in range(a, b, c)]

or

def gen_range_2(a, b, c):
    s = range(a, b + c, c)
    return zip(s, s[1:])

See http://ideone.com/VhY215

If (b - a) % c != 0:

def gen_range(a, b, c):
    return [(i, i + c) for i in range(a, b - ((b - a) % c), c)]

or

def gen_range_2(a, b, c):
    s = range(a, b - ((b - a) % c) + c, c)
    return zip(s, s[1:])

See http://ideone.com/i3Pq69

like image 73
irrelephant Avatar answered Sep 24 '22 20:09

irrelephant


A really short way:

ranges = [(n, min(n+step, stop)) for n in xrange(start, stop, step)]

More verbose ways:

Perhaps via a generator?

def gen_range(start, stop, step):
    current = start
    while current < stop:
        next_current = current + step
        if next_current < stop:
            yield (current, next_current)
        else:
            yield (current, stop)
        current = next_current

Calling this function gives you a generator object that will produce each of the tuples in order. You would use it like this:

for block in gen_range(100000000,600000000,100):
    print block

which would output...

(100000000,100000100)
(100000100,100000200)
(100000200,100000300)
...
(599999900,600000000)

You could do this even more simply with a generator expression if you're always certain that stop-start is an even multiple of the step:

ranges = ((n, n+step) for n in xrange(start, stop, step))

# Usage
for block in ranges:
    print block

Also note that if you want to turn a generator into a list, holding the entirety of the results in memory, you can simply pass it to list():

all_ranges = list(gen_range(100000000,600000000,100))
like image 36
Amber Avatar answered Sep 23 '22 20:09

Amber


something like this:

In [48]: it=iter(xrange(100,200,10))

In [49]: it1=iter(xrange(110,210,10))

In [50]: [(next(it),next(it1)) for _ in range(10)]
Out[50]: 
[(100, 110),
 (110, 120),
 (120, 130),
 (130, 140),
 (140, 150),
 (150, 160),
 (160, 170),
 (170, 180),
 (180, 190),
 (190, 200)]

or using zip():

In [55]: it=iter(xrange(100,200,10))

In [56]: it1=iter(xrange(110,210,10))

In [57]: [(x,y) for x,y in zip(it,it1)]
Out[57]: 
[(100, 110),
 (110, 120),
 (120, 130),
 (130, 140),
 (140, 150),
 (150, 160),
 (160, 170),
 (170, 180),
 (180, 190),
 (190, 200)]
like image 42
Ashwini Chaudhary Avatar answered Sep 22 '22 20:09

Ashwini Chaudhary


I'd make it a generator so it could handle huge ranges.

def gen_range(start, stop, step):
    a, b = start, start+step
    while b <= stop:
        yield a, b
        a, b = b, b+step

print list(gen_range(100, 140, 10))
like image 40
martineau Avatar answered Sep 23 '22 20:09

martineau