Tutorial :Interpreting Number Ranges in Python



Question:

In a Pylons webapp, I need to take a string such as "<3, 45, 46, 48-51, 77" and create a list of ints (which are actually IDs of objects) to search on.

Any suggestions on ways to do this? I'm new to Python, and I haven't found anything out there that helps with this kind of thing.

The list would be: [1, 2, 3, 45, 46, 48, 49, 50, 51, 77]


Solution:1

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])  


Solution:2

rng = "<3, 45, 46, 48-51, 77"  ids = []  for x in map(str.strip,rng.split(',')):      if x.isdigit():          ids.append(int(x))          continue      if x[0] == '<':          ids.extend(range(1,int(x[1:])+1))          continue      if '-' in x:          xr = map(str.strip,x.split('-'))          ids.extend(range(int(xr[0]),int(xr[1])+1))          continue      else:          raise Exception, 'unknown range type: "%s"'%x  


Solution:3

First, you'll need to figure out what kind of syntax you'll accept. You current have three in your example:

  1. Single number: 45, 46

  2. Less than operator

  3. Dash ranging: 48-51

After that, it's just a matter of splitting the string into tokens, and checking the format of the token.


Solution:4

>>> print range.__doc__  range([start,] stop[, step]) -> list of integers  

Return a list containing an arithmetic progression of integers. range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0. When step is given, it specifies the increment (or decrement). For example, range(4) returns [0, 1, 2, 3]. The end point is omitted! These are exactly the valid indices for a list of 4 elements.

>>> range(33,44)  [33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43]  >>> range(1,3)  [1, 2]  

I imagine you could iterate your list, and call range appropriately.

>>> def lessThan(n) :  ...  return range(n+1)  ...  >>> lessThan(4)  [0, 1, 2, 3, 4]  >>> def toFrom(n,m):  ...  return range(n,m)  ...  >>> toFrom(33,44)  [33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43]  

Then split the string on commas, and for each bit, parse it enough to figure out what function to call, catenating the lists returned.

Anything more and I'd have written it for you.


Solution:5

I also had to do something similar for an app lately.

If you don't need concrete numbers but just a way to see whether a given number is in the range, you might consider parsing it to a Python expression you can eval into a lambda. For example <3, 5-10, 12 could be func=(lambda x:x<3 or (5 <= x <= 10) or x==12)). Then you can just call the lambda, func(11) to see if 11 belongs in there.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »