12
\$\begingroup\$

I have input of a string containing a single number (like: \$3\$) or a range (like: \$1-5\$). Sample input, all together, looks like: "1-5,3,15-16", and sample output for that input looks like "1,2,3,4,5,15". Output doesn't need to be sorted.

I built something to parse this, but it's ugly. How can I improve this?

from itertools import chain
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!")

def unpackNums(numString:str):
    rList=[]
    rList.extend(set(chain(*map(giveRange,numString.split(",")))))
    return rList
unpackNums("1-2,30-50,1-10")
\$\endgroup\$
0

2 Answers 2

6
\$\begingroup\$

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.

\$\endgroup\$
0
8
\$\begingroup\$

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.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ This solution does not remove duplicate values. An input of "1-2,30-50,1-10" will yield an output of 1,2,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,1,2,3,4,5,6,7,8,9,10 \$\endgroup\$ Commented Nov 12, 2023 at 2:35

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.