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")
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Aug 20, 2015 at 18:09
\$\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.

Quill
12k5 gold badges41 silver badges93 bronze badges
answered Aug 20, 2015 at 18:37
\$\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.

answered Aug 20, 2015 at 18:27
\$\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

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.