28

I would like to be able to convert a string such as "1,2,5-7,10" to a python list such as [1,2,5,6,7,10]. I looked around and found this, but I was wondering if there is a clean and simple way to do this in Python.

8 Answers 8

40
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]
Sign up to request clarification or add additional context in comments.

2 Comments

This would make a nice generator.
Not exploding on leading or trailing comma is achieved by replacing the else with elif part != '':
7

I was able to make a true comprehension on that question:

>>> def f(s):
    return sum(((list(range(*[int(j) + k for k,j in enumerate(i.split('-'))]))
         if '-' in i else [int(i)]) for i in s.split(',')), [])

>>> f('1,2,5-7,10')
[1, 2, 5, 6, 7, 10]

>>> f('1,3-7,10,11-15')
[1, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15]

the other answer that pretended to have a comprehension was just a for loop because the final list was discarded. :)

For python 2 you can even remove the call to list!

Comments

4

This might be an overkill, but I just like pyparsing:

from pyparsing import *

def return_range(strg, loc, toks):
    if len(toks)==1:
        return int(toks[0])
    else:
        return range(int(toks[0]), int(toks[1])+1)
def parsestring(s):
    expr = Forward()
    term = (Word(nums) + Optional(Literal('-').suppress() + Word(nums))).setParseAction(return_range)
    expr << term + Optional(Literal(',').suppress() + expr)
    return expr.parseString(s, parseAll=True)

if __name__=='__main__':
    print parsestring('1,2,5-7,10')

Comments

3

No comprehension beats mine!

import re
def convert(x):
    return sum((i if len(i) == 1 else list(range(i[0], i[1]+1))
               for i in ([int(j) for j in i if j] for i in
               re.findall('(\d+),?(?:-(\d+))?', x))), [])

The best part is that I use variable i twice in the middle of the comprehension.

>>> convert('1,2,5-7,10')
[1, 2, 5, 6, 7, 10]

Comments

2

Very short, and elegant (imho):

>>> txt = "1,2,5-7,10"
>>> # construct list of xranges
>>> xranges = [(lambda l: xrange(l[0], l[-1]+1))(map(int, r.split('-'))) for r in txt.split(',')]
>>> # flatten list of xranges
>>> [y for x in xranges for y in x]
[1, 2, 5, 6, 7, 10]

Comments

2

From here: https://gist.github.com/raczben/76cd1229504d82115e6427e00cf4742c

def number(a, just_try=False):
    """
    Parse any representation of number from string.
    """
    try:
        # First, we try to convert to integer.
        # (Note, that all integer can be interpreted as float and hex number.)
        return int(a)
    except:
        # The order of the following convertions doesn't matter.
        # The integer convertion has failed because `a` contains hex digits [x,a-f] or a decimal
        # point ['.'], but not both.
        try:
            return int(a, 16)
        except:
            try:
                return float(a)
            except:
                if just_try:
                    return a
                else:
                    raise


def str2numlist(s):
    """
    Convert a string parameter to iterable object.
    """
    return [y for x in s.split(',') for y in str_ranges_to_list(x) ]


def str_ranges_to_list(s):
    """
    Convert a string parameter to iterable object.
    """
    s = s.strip()
    try:
        begin,end=s.split(':')
        return range(number(begin), number(end))
    except ValueError: # not enough values to unpack
        return [number(s)]

Comments

1

Ugh, the answers are so verbose! Here is a short and elegant answer:

def rangeString(commaString):
    def hyphenRange(hyphenString):
        x = [int(x) for x in hyphenString.split('-')]
        return range(x[0], x[-1]+1)
    return chain(*[hyphenRange(r) for r in commaString.split(',')])

Demo:

>>> list( f('1,2,5-7,10') )
[1, 2, 5, 6, 7, 10]

Easily modifiable to handle negative numbers or return a list. Also will need from itertools import chain, but you can substitute sum(...,[]) for it if you are not working with range objects (or sum(map(list,iters),[])) and you don't care about being lazy.

Comments

1

Initializing string

test_str = "1, 4-6, 8-10, 11"

printing original string

print("The original string is : " + test_str)

Convert String ranges to list

res = sum(((list(range(*[int(b) + c 
           for c, b in enumerate(a.split('-'))]))
           if '-' in a else [int(a)]) for a in test_str.split(', ')), [])

printing result

print("List after conversion from string : " + str(res))

1 Comment

this will fail when test_str is 1,2 4-6, 8-10, 11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.