def f(x):
    result = []
    for part in x.split(','):
        if '-' in part:
            a, b = part.split('-')
            a, b = int(a), int(b)
            result.extend(range(a, b + 1))
        else:
            a = int(part)
            result.append(a)
    return result

>>> f('1,2,5-7,10')
[1, 2, 5, 6, 7, 10]
Answer from FogleBird on Stack Overflow
Top answer
1 of 2
6

Disclaimer: I'm not a Python programmer!


Your code isn't that bad. It's readable enough for me to understand it. B+ for readability!


Currently, you have this function:

def giveRange(numString:str):
    z=numString.split("-")
    if(len(z)==1):
        return [int(z[0])]
    elif(len(z)==2):
        return list(range(int(z[0]),int(z[1])+1))
    else:
        raise IndexError("TOO MANY VALS!")

Why don't you simply store the length in a variable?

Like this:

length=len(z)
if(length==1):
    return [int(z[0])]
elif(length==2):
    return list(range(int(z[0]),int(z[1])+1))
else:
    raise IndexError("TOO MANY VALS!")

Now, you don't have to calculate the length twice, only once.


The name z is a really bad name. Better names would be numbers, pieces or something similar.


Looking at the definition of chain(), it seems to accept any iterable, which a range() happens to be. So, you probably don't need that list(), leaving this:

return range(int(z[0]),int(z[1])+1)

On your function unpackNums instead of creating an empty set(), you could use the a set comprehension:

def unpackNums(numString:str):
    return {x for x in set(chain(*map(giveRange,numString.split(","))))}

If you notice any inaccuracies, please comment.

2 of 2
8

Since the whole exercise is a string-transformation problem, I suggest performing it using a regex substitution.

import re

def expand_ranges(s):
    return re.sub(
        r'(\d+)-(\d+)',
        lambda match: ','.join(
            str(i) for i in range(
                int(match.group(1)),
                int(match.group(2)) + 1
            )   
        ),  
        s
    )

I think that expand_ranges would be a more descriptive name than unpackNums.

Top answer
1 of 6
12

Use parseIntSet from here

I also like the pyparsing implementation in the comments at the end.

The parseIntSet has been modified here to handle "<3"-type entries and to only spit out the invalid strings if there are any.

#! /usr/local/bin/python
import sys
import os

# return a set of selected values when a string in the form:
# 1-4,6
# would return:
# 1,2,3,4,6
# as expected...

def parseIntSet(nputstr=""):
    selection = set()
    invalid = set()
    # tokens are comma seperated values
    tokens = [x.strip() for x in nputstr.split(',')]
    for i in tokens:
        if len(i) > 0:
            if i[:1] == "<":
                i = "1-%s"%(i[1:])
        try:
            # typically tokens are plain old integers
            selection.add(int(i))
        except:
            # if not, then it might be a range
            try:
                token = [int(k.strip()) for k in i.split('-')]
                if len(token) > 1:
                    token.sort()
                    # we have items seperated by a dash
                    # try to build a valid range
                    first = token[0]
                    last = token[len(token)-1]
                    for x in range(first, last+1):
                        selection.add(x)
            except:
                # not an int and not a range...
                invalid.add(i)
    # Report invalid tokens before returning valid selection
    if len(invalid) > 0:
        print "Invalid set: " + str(invalid)
    return selection
# end parseIntSet

print 'Generate a list of selected items!'
nputstr = raw_input('Enter a list of items: ')

selection = parseIntSet(nputstr)
print 'Your selection is: '
print str(selection)

And here's the output from the sample run:

$ python qq.py
Generate a list of selected items!
Enter a list of items: <3, 45, 46, 48-51, 77
Your selection is:
set([1, 2, 3, 45, 46, 77, 48, 49, 50, 51])
2 of 6
4

I've created a version of @vartec's solution which I feel is more readable:

def _parse_range(numbers: str):
    for x in numbers.split(','):
        x = x.strip()
        if x.isdigit():
            yield int(x)
        elif x[0] == '<':
            yield from range(0, int(x[1:]))
        elif '-' in x:
            xr = x.split('-')
            yield from range(int(xr[0].strip()), int(xr[1].strip())+1)
        else:
            raise ValueError(f"Unknown range specified: {x}")

In the process, the function became a generator :)

🌐
GitHub
github.com › markusressel › py-range-parse
GitHub - markusressel/py-range-parse: Parses commonly used range notations to python objects · GitHub
py-range-parse is a library to parse commonly used range notations to python objects that act like sets.
Starred by 2 users
Forked by 2 users
Languages   Python 99.9% | Makefile 0.1%
🌐
GitHub
gist.github.com › kgaughan › 2491663
Parsing a comma-separated list of numbers and range specifications in Python · GitHub
What this code is intended to do is take a string like '2-5,7,15-17,12' and turn it into the list [2, 3, 4, 5, 7, 12, 15, 16, 17]. Simple enough. Also interview pre-screening kryptonite, it seems. ... Just what I was looking for. Thanks! ... Two thumbs up! Thanks! ... Nice, Thanks for posting! ... from itertools import chain def parse_range_list(rl): def parse_range(r): if len(r) == 0: return [] parts = r.split("-") if len(parts) > 2: raise ValueError("Invalid range: {}".format(r)) return range(int(parts[0]), int(parts[-1])+1) return sorted(set(chain.from_iterable(map(parse_range, rl.split(",")))))
🌐
GeeksforGeeks
geeksforgeeks.org › python-convert-string-ranges-to-list
Python | Convert String ranges to list | GeeksforGeeks
May 16, 2023 - # Python2 code to demonstrate working of # Convert String ranges to list # Using map() + lambda + split() # initializing string test_str = &quot;1, 4-6, 8-10, 11&quot; # printing original string print(&quot;The original string is : &quot; + test_str) # Convert String ranges to list # Using map() + lambda + split() temp = [(lambda sub: range(sub[0], sub[-1] + 1))(map(int, ele.split('-')))\ for ele in test_str.split(', ')] res = [b for a in temp for b in a] # printing result print(&quot;List after conversion from string : &quot; + str(res))
🌐
PyPI
pypi.org › project › py-range-parse
py-range-parse · PyPI
py-range-parse is a library to parse commonly used range notations to python objects that act like sets.
      » pip install py-range-parse
    
Published   Oct 12, 2025
Version   1.0.6
🌐
Darklaunch
darklaunch.com › python-parse-range-and-parse-group-range.html
Python Parse Range and Parse Group Range - Dark Launch
November 5, 2012 - from itertools import groupby from ... group = map(itemgetter(1), g) ranges.append((group[0], group[-1])) return ranges def parse_range(astr): """ Return a range list given a string....
Find elsewhere
🌐
Rosetta Code
rosettacode.org › wiki › Range_expansion
Range expansion - Rosetta Code
June 2, 2026 - (defun expand-ranges (string) (loop with prevnum = nil for idx = 0 then (1+ nextidx) for (number nextidx) = (multiple-value-list (parse-integer string :start idx :junk-allowed t)) append (cond (prevnum (prog1 (loop for i from prevnum to number collect i) (setf prevnum nil))) ((and (< nextidx (length string)) (char= (aref string nextidx) #\-)) (setf prevnum number) nil) (t (list number))) while (< nextidx (length string)))) CL-USER> (expand-ranges "-6,-3--1,3-5,7-11,14,15,17-20") (-6 -3 -2 -1 3 4 5 7 8 9 10 11 14 15 17 18 19 20)
🌐
PyPI
pypi.org › project › pynumparser
Client Challenge
JavaScript is disabled in your browser · Please enable JavaScript to proceed · A required part of this site couldn’t load. This may be due to a browser extension, network issues, or browser settings. Please check your connection, disable any ad blockers, or try using a different browser
🌐
GitHub
github.com › Elkip › PythonMorsels › blob › master › parse_ranges.py
PythonMorsels/parse_ranges.py at master · Elkip/PythonMorsels
This week I'd like you to write a function called parse_ranges, which accepts a string containing ranges
Author   Elkip
🌐
Python Examples
pythonexamples.org › python-convert-numeric-string-range-to-a-list
How to Convert Numeric String Range to a List in Python?
# Take a numeric string range x = '4-9' # Split the string by hypehn a, b = x.split('-') # Convert string splits into integers a, b = int(a), int(b) # Create a range from the given values result = range(a, b + 1) # Convert the range into list result = list(result) print(result) ...
🌐
Stanford CS
cs.stanford.edu › people › nick › py › python-range.html
Python range() Function
The most common form is range(n), given integer n returns a numeric series starting with 0 and extending up to but not including n, e.g. range(6) returns 0, 1, 2, 3, 4, 5. With Python's zero-based indexing, the contents of a string length 6, are at index numbers 0..5, so range(6) will produce exactly the index numbers for a string length 6, like this:
🌐
PyPI
pypi.org › project › DateRangeParser
DateRangeParser · PyPI
DateRangeParser is a Python module which makes it easy to parse date ranges specified in a human-style string.
      » pip install DateRangeParser
    
Published   May 26, 2019
Version   1.3.2
🌐
PyPI
pypi.org › project › RangeParser
RangeParser · PyPI
January 23, 2012 - Parses ranges. ... RangeParser is a Python package to parse ranges easily.
      » pip install RangeParser
    
Published   Jan 23, 2012
Version   0.1.3
🌐
Robin's Blog
blog.rtwilson.com › announcing-daterangeparser-parse-strings-like-27th-29th-june-2010
Announcing DateRangeParser: Parse strings like “27th-29th June 2010” « Robin's Blog
May 1, 2012 - The API is very simple – just import the parse method and run it, giving the date range string as an argument. For example: from daterangeparser import parse print parse("14th-19th Feb 2010") This will produce an output tuple with two datetime objects in it: the start and end date of the ...
🌐
Blogger
thoughtsbyclayg.blogspot.com › 2008 › 10 › parsing-list-of-numbers-in-python.html
thoughts by clayg: Parsing a list of numbers in Python
This is a fun little parsing problem. Couldn't resist trying a pyparsing version. What do you think? ============ from pyparsing import Word,nums,delimitedList # define basic expressions for an integer or range of integers integer = Word(nums) intRange = integer + "-" + integer # define expression for comma-delimited list of intRange or integer integerList = delimitedList(intRange | integer, ",") # convert string to int integer.setParseAction(lambda t:int(t[0])) # convert range to list of ints intRange.setParseAction(lambda t: range(t[0],t[2]+1) if t[0]>t[2] else range(t[2],t[0]+1) if t[2]>t[0] else t[0]) # sort total list, use set to remove duplicates integerList.setParseAction(lambda t: sorted(set(t.asList()))) print integerList.parseString("1-4,6,3-2, 11, 8 - 12,5,14-14") ================= Prints: [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 14] -- Paul