0

This tells us the way to prepare a list of numbers, including a range of continues numbers or discontinues numbers.

For example, list(range(1,10)) + [20,30,40] + list(range(400,410)) will return [1, 2, 3, 4, 5, 6, 7, 8, 9, 20, 30, 40, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409].

But can I make it simpler by only inputting a string like "1-10, 20, 30, 40, 400-410"?

I can write a function using a for-loop to go through the numbers. Is there a more efficient way to write the function without using a for-loop?

lanselibai
  • 1,203
  • 2
  • 19
  • 35
  • 2
    You'd have to parse the String. That will be much more difficult, and less efficient. Unless you need to do something like this constantly, you should just stick to the first way. – Carcigenicate Mar 19 '18 at 15:08
  • Why do you think it is not simple enough? – David Leon Mar 19 '18 at 15:10
  • You can always include things like your 2nd set of intervals using range with the step argument like `list(range(20,50,10))` – Chris_Rands Mar 19 '18 at 15:11
  • The first way is the simplest way to represent the list of numbers in Python code. The second way is the simplest way for a user to input what they want, since you can't expect them to type code. But if you have to accept that sort of user input, you're going to have to translate it into Python code very similar to the first way before you use it. – BoarGules Mar 19 '18 at 15:11
  • See https://stackoverflow.com/questions/6405208/how-to-convert-numeric-string-ranges-to-a-list-in-python for some implementations of a parser – stephan Mar 19 '18 at 15:13
  • 3
    You can do something such as: `[*range(1, 10), 20, 30, 40, *range(400, 410)]` as well... – Jon Clements Mar 19 '18 at 15:16
  • thank you! @stephan – lanselibai Mar 19 '18 at 15:16
  • Or even `[*range(1, 10), *range(20, 50, 10), *range(400, 410)]` – Olivier Melançon Mar 19 '18 at 15:17

3 Answers3

1

You can use a nested list comprehension, checking whether the current part contains a - and using either a range or creating a one-elemented list accordingly:

>>> s = "1-10, 20, 30, 40, 400-410"
>>> [n for part in s.split(", ") for n in (range(*map(int, part.split("-"))) if "-" in part else [int(part)])]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 20, 30, 40, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409]

Maybe split this up, for readability:

>>> to_list = lambda part: range(*map(int, part.split("-"))) if "-" in part else [int(part)]
>>> [n for part in s.split(", ") for n in to_list(part)]

Note: As in your first example, this will translate "1-10" to [1, 2, ..., 9], without 10.


As noted in comments, this will not work for negative numbers, though, trying to split -3 or -4--2 into pairs of numbers. For this, you could use regular expressions...

>>> def to_list(part):
...     m =re.findall(r"(-?\d+)-(-?\d+)", part)
...     return range(*map(int, m[0])) if m else [int(part)]
...
>>> s = "-10--5, -4, -2-3"
>>> [n for part in s.split(", ") for n in to_list(part)]
[-10, -9, -8, -7, -6, -4, -2, -1, 0, 1, 2]

... or just use a different delimiter for ranges, e.g. -10:-5.

tobias_k
  • 81,265
  • 12
  • 120
  • 179
  • 2
    If the OP ever needs a negative starting range - they'll find it interesting adapting this :) – Jon Clements Mar 19 '18 at 15:27
  • @JonClements Good point, also with single negative numbers. Just using `"-" in part` will not work then. – tobias_k Mar 19 '18 at 15:36
  • 1
    Probably make it more easily extendable to use `:` which also mirrors slice syntax... but meh... just pointing it out for future visitors. I doubt it'll be a concern for the OP – Jon Clements Mar 19 '18 at 15:38
0

I suggest that you instead write a function that allows to create more complex, piecewise ranges.

def piecewise_range(*args):
    for arg in args:
        yield from range(*arg)

piecewise_range((1, 10), (20, 50, 10), (400, 410))

The function piecewise_range then outputs a generator, so it allows you to have more complex range patterns and to preserve range laziness.

Olivier Melançon
  • 21,584
  • 4
  • 41
  • 73
0

Depending on where your input strings come from, you might do with something like this, named whatever you would want to name it:

import re

def gen_nums(input_string):
    out = []
    for item in re.split(r',\s*', input_string):
        if '-' in item:
            start, end = item.split('-')
            out.extend(range(int(start), int(end)))
        else:
            out.append(int(item))
    return out
ryanmrubin
  • 728
  • 4
  • 11