DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Snippets has posted 5883 posts at DZone. View Full User Profile

STEP - Alpha-numeric String Incrementer

05.29.2006
| 2995 views |
  • submit to reddit
        
    step: func [ ; INCR SUCC ADVANCE NEXT-STR, inspired by Ruby's succ method.
        "Increments strings, skipping non-alpha-num chars; doesn't auto-grow if first char is not alpha-num."
        series [string!]
        /local digit= alpha= alpha-num= =val carry? step-val mark
               range-start-char range-start-char? grow-if-necessary
    ][
        if empty? series [return series]
        digit=: charset [#"0" - #"9"]
        alpha=: charset [#"A" - #"Z" #"a" - #"z"]
        alpha-num=: union alpha= digit=
        carry?: does [find "9Zz" =val]
        range-start-char?: func [val] [find "0Aa" val]
        ; This gives us the new "rollover digit" to add, when we need
        ; to grow a string. Zeros rolling to ones is the exception to
        ; the rule; otherwise, we just add the same char that's there
        ; (the starting char for a range).
        ;
        range-start-char: func [val] [
            select/case [#"0" #"1"  #"A" #"A"  #"a" #"a"] val
        ]
        grow-if-necessary: does [
            if range-start-char? last series [
                append series range-start-char last series
            ]
        ]
        ; Increment a char val, rolling over if necessary.
        step-val: func [val] [
            either find "9Zz" val [
                select/case [#"9" #"0"  #"Z" #"A"  #"z" #"a"] val
            ] [add val 1]
        ]
        catch [
            ; Reverse the series, so we can parse left to right.
            parse/case reverse series [
                some [
                    mark:
                    copy =val alpha-num= (
                        change mark step-val to char! =val
                        ; Carry? means the char we just found will cause
                        ; a "carry" to the next digit, so we keep going;
                        ; otherwise, we're done
                        if not carry? [throw]
                    )
                    ; We just skip over non-alph-num chars in the current
                    ; design.
                    | skip
                ]
            ]
            ; If we stopped carrying at some point, we won't get here.
            grow-if-necessary
        ]
        ; We reversed the series to parse it, so reverse it again.
        reverse series
    ]
;     test-step: func [val expected-result] [
;         if expected-result <> step copy val [
;             print ["Test Failed:" mold val mold step copy val]
;         ]
;     ]
;     foreach [val res] [
;         ""    ""
;         "!"   "!"
;         "!@#$%^&*()" "!@#$%^&*()"
;         "aa"  "ab"
;         "aaa" "aab"
;         "az"  "ba"
;         "aZ"  "bA"
;         "zz"  "aaa"
;         "#zz" "#aa"
;         "ZZ"  "AAA"
;         "!ZZ" "!AA"
;         "001" "002"
;         "009" "010"
;         "099" "100"
;         "999"       "1000"
;         "~999"      "~000"
;         "123@999"   "124@000"
;         "1.2.3"     "1.2.4"
;         "001#1.2.9" "001#1.3.0"
;         "001#9.9.9" "002#0.0.0"
;         "001-zzz"   "002-aaa"
;     ] [test-step val res]