Alphanumeric key incrementing routine

topic: 

Creates next string key from the passed one using specified set of characters

$SAMPLECODE$

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
 

*
* Creates next string key from the passed one using specified set of characters
*
* Parameters
*	1 - Current Key
*	2 - Defines the list of allowed characters
*		BASEnn - See DO CASE in the body of the program
*		CUSTOM - the list of character as parameter 3
*	3 - List of characters
*
* Returns 	Next key
*
*   Note 1  Routine ignores (doesn't change) positions with the characters not in the specified list
*   Note 2  When max possible value is reached, the next return value will be the min possible value
*

FUNCTION NextKey
LPARAMETERS tcOldVal, tcOpt, tcCharList
LOCAL lcNewVal, i, lcDigits, lcLetters, lnCharListLen, lcOldChar, lcNewChar, lcCharList, lnPos, lcOpt
LOCAL lnNextPos

lcOpt = IIF(EMPTY(tcOpt), "BASE10", UPPER(tcOpt))
* Get the list of appropriate characters
lcCharList = NextKeyFillCharList(lcOpt, tcCharList)
lnCharListLen = LEN(lcCharList)
lcNewVal = tcOldVal

* Scan string from the right to the left
FOR i = LEN(lcNewVal) TO 1 STEP -1

	lcOldChar = SUBSTR(tcOldVal, i, 1)
	* Is the current charater in the list?
	lnPos = AT(lcOldChar, lcCharList)
	IF lnPos = 0
		* Not in the list, keep it
		LOOP
	ENDIF

	* Get the next character position
	lnNextPos = (lnPos % lnCharListLen) + 1
	* Get the next character 
	lcNewChar = SUBSTR(lcCharList,lnNextPos,1)
	* Stuff it back in the string
	lcNewVal = STUFF(lcNewVal, i, 1, lcNewChar)
	* Check if we have to carry over to the next position
	IF lnNextPos > 1
		* We are done
		EXIT
	ENDIF

ENDFOR

RETURN lcNewVal
*------------------------------------------------------------
* Fill the list of characters based on character set requested
FUNCTION NextKeyFillCharList
LPARAMETERS tcCharSet, tcCharList
LOCAL lcCharList, lcDigits, lcLetters
* Fill string 'lcCharList' with appropriate characters
lcDigits = "0123456789"
lcLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

DO CASE
CASE tcCharSet = "CUSTOM"  
	lcCharList = tcCharList
CASE tcCharSet == "BASE10" 
	* Just Digits
	lcCharList = lcDigits
CASE tcCharSet == "BASE16L" 
	* Hexadecimal in lower case
	lcCharList = lcDigits + "abcdef"
CASE tcCharSet == "BASE16" 
	* Hexadecimal in upper case
	lcCharList = lcDigits + "ABCDEF"
CASE tcCharSet == "BASE26L" 
	* Lower case letters
	lcCharList = LOWER(lcLetters)
CASE tcCharSet == "BASE26" 
	* Upper case letters
	lcCharList = lcLetters
CASE tcCharSet == "BASE36L" 
	* Digits + Lower case letters
	lcCharList = lcDigits + LOWER(lcLetters)
CASE tcCharSet == "BASE36" 
	* Digits + Upper case letters
	lcCharList = lcDigits + lcLetters
CASE tcCharSet == "BASE52" 
	* All letters
	lcCharList = lcLetters + LOWER(lcLetters)
CASE tcCharSet == "BASE62" 
	* Digits + All letters
	lcCharList = lcDigits + lcLetters + LOWER(lcLetters)
OTHERWISE
	* The same as BASE10
	lcCharList = lcDigits
ENDCASE
RETURN lcCharList 

Examples
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
* Rollover to min value (Base10)
? NextKey("999999")			&& 000000

* Next value (Base16)
? NextKey("999999", "Base16")  && 99999A

* Dash ('-') has not been changed
? NextKey("999-FFF", "Base16")  && 99A-000

Comments

Thank you for sharing, it´s really helpful. I´ve been using it for some time with no problems. Thanks again!