The Mod Out System
June 23, 2009
Dan Schoch provides this PL/SQL function:
-- local variables
token_pointer pls_integer := 1 ;
-- get a token from a string (either integer, - or comma) -------- get_token
function get_token( parm_str in varchar2) return varchar2 is
-- returns Integer as varchar2, (C)omma, (D)ash, (O)ther
my_pointer pls_integer := 0 ;
num pls_integer := 0 ;
begin
case when substr(parm_str,token_pointer,1) = ','
then token_pointer := token_pointer + 1 ;
return 'C' ;
when substr(parm_str,token_pointer,1) = '-'
then token_pointer := token_pointer + 1 ;
return 'D' ;
else while (token_pointer <= my_pointer+length(parm_str)
and substr(parm_str,token_pointer+my_pointer,1) not in (',','-'))
loop num := num*10 + to_number(substr(parm_str,token_pointer+my_pointer,1)) ;
my_pointer := my_pointer + 1 ;
end loop ;
token_pointer := token_pointer + my_pointer ;
return to_char(num,'fm999999999') ;
end case ;
exception when others then return 'O' ;
end get_token ;
-- parses string to list of numbers ----------------------------- parse_nums
function parse_nums(parm_str in varchar2) return varchar2 is
ret_str varchar2(4000) := null ;
cur_token varchar2(12) := null ;
last_token varchar2(12) := 'C' ;
start_range pls_integer := null ;
begin
token_pointer := 1 ;
while (token_pointer <= length(parm_str))
loop
cur_token := get_token(parm_str) ;
case
when cur_token not in ('C','D','O') and last_token = 'C'
then ret_str := ret_str||cur_token||',' ;
when cur_token not in ('C','D','O') and last_token = 'D'
then for i in start_range+1..to_number(cur_token)
loop ret_str := ret_str||to_char(i,'fm999999999')||',' ;
end loop ;
when cur_token = 'C' and last_token not in ('C','D','O')
then null ;
when cur_token = 'D' and last_token not in ('C','D','O')
then start_range := to_number(last_token) ;
else p.l('error at position '||to_char(token_pointer,'fm999,999')) ;
end case ;
last_token := cur_token ;
end loop ;
return substr(ret_str,1,length(ret_str)-1) ;
exception when others then return 'error at position '||to_char(token_pointer,'fm999,999') ;
end parse_nums ;
A Scheme solution is shorter and clearer:
(define (make-range s)
(if (not (member #\- (string->list s))) s
(let ((ab (string-split #\- s)))
(string-join #\,
(map number->string
(range (string->number (car ab))
(+ (string->number (cadr ab)) 1)))))))
(define (mod-out str)
(string-join #\, (map make-range (string-split #\, str))))
Make-range
expands a range, mod-out
calls make-range
for each entry in the list. String-split
, string-join
and range
come from the Standard Prelude. It looks like this:
> (mod-out "1-6,9,13-19")
"1,2,3,4,5,6,9,13,14,15,16,17,18,19"
You can run this program at http://programmingpraxis.codepad.org/5rArlKwi.
[…] Praxis – The Mod Out System By Remco Niemeijer Today’s Programming Praxis problem is an easy one: all we have to do is convert a string that contains […]
My Haskell solution (see http://bonsaicode.wordpress.com/2009/06/23/programming-praxis-the-mod-out-system/ for a version with comments):
In Scala:
object ModOutSystem {
def modOut(s: String) =
s split “,” flatMap { _ split “-” match {
case Array(single) => List(single.toInt)
case Array(from, to) => List.range(from.toInt, to.toInt + 1)
} }
def main(args: Array[String]) = println(modOut(“1-6,9,13-19”).deepToString) // Array(1, 2, 3, 4, 5, 6, 9, 13, 14, 15, 16, 17, 18, 19)
}
Pretty nice post. I just came by your blog and wanted to say
that I have really enjoyed reading your posts. Any way
I’ll be subscribing to your feed and I hope you post again soon!
In Python2.x:
def combineranges(a):
b = [[int(j) for j in i.split(“-“)] for i in a.split(“,”)]
c = []
[c.extend(range(b[i][0],b[i][len(b[i])-1]+1)) for i in range(len(b))]
return c
print combineranges(“1-6,9,13-19”)
In Ruby:
def expand(s)
s.inject {|x,y| x..y}.to_a
end
def mod_out(mod_string)
mod_string.split(“,”).collect {|x| x.split(“-“)}.collect {|x| expand(x)}.join(“,”)
end
puts mod_out “1-6,9,13-19”
A quick hack in Common Lisp.
(defun mod-out (ranges)
(flatten
(loop for range in ranges
collect (if (consp range)
(loop for i from (car range) to (cdr range) collect i)
range))))
;; usage
(mod-out ‘((1 . 6) 9 (13 . 19)))
Take FLATTEN from your favorite utility library or write it yourself.