27

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.

Community
  • 1
  • 1
jncraton
  • 9,022
  • 3
  • 34
  • 49

8 Answers8

39
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]
Basj
  • 41,386
  • 99
  • 383
  • 673
FogleBird
  • 74,300
  • 25
  • 125
  • 131
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!

JBernardo
  • 32,262
  • 10
  • 90
  • 115
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')
Graham
  • 3,153
  • 3
  • 16
  • 31
Hans
  • 1,741
  • 3
  • 25
  • 38
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]
Kenneth Hoste
  • 2,816
  • 20
  • 14
2

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]
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)]
betontalpfa
  • 3,454
  • 1
  • 33
  • 65
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.

ninjagecko
  • 88,546
  • 24
  • 137
  • 145
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))
Akash Kumar
  • 540
  • 2
  • 4
  • 15