increment a string that has leading zeros

Hi all.

this one has me stumped:

I know how to pad a number with leading zeros but in this instance I would like to increment a number that has leading zeros and return a string:

set myVar to "0033620340000"

repeat 10 times
	set myVar to myVar + 1
end repeat

I would like myVar to be returned as a string like it was originally set i.e 0033620340010 but that’s not what the above script gives me.

another thing is that I don’t always know how many leading zeros there will be.

Any help will be greatly appreciated.

Thanks,
Nik

Something like this?

set myVar to "000999999"

set myNum to (myVar as number) + 1
set newNum to {}

repeat until myNum < 1
	set beginning of newNum to myNum mod 10 as integer
	set myNum to myNum div 10
end repeat

repeat (length of myVar) - (length of newNum) times
	set beginning of newNum to 0
end repeat

set {oldTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, ""}
set newVar to newNum as string
set AppleScript's text item delimiters to oldTID
return newVar

EDIT: I had an error in my previously code. When you have the number as in this example the number of leading zero’s would remain three while it should be 2. That’s been fixed now.

EDIT 2: Nigel indicated a safety leak when newNum is coerced into a string when the delimiters are not set to “”. Now the delimiters are temporarily set to “” before the coercion takes place and then set the delimiters back to it’s previous value.

This does indeed do it.

Thanks,
Nik

Hi DJ.

Don’t forget the explicit delimiter for safety.

Thanks Nigel, I forgot about the delimiters and updated my Script above.

Here is another take, which may be less readable, but faster in loops with over 1000 iterations.

property zeroes : "0000000000" -- 10 digits should be enough for most purposes.
# This solution only works for numbers smaller than  2^29 which is 536 870 912 decimal.
# and you will of course have to add a considerable number of zeroes.

set myVar to "000999999"
set myVar to incrementZ(myVar)
on incrementZ(zeroedNum)
	tell zeroedNum to set {zeroFill, numlen} to {text 1 thru (length of it) of zeroes, (length of it)}
	
	tell (a reference to text item delimiters) to set {tids, contents of it} to {contents of it, ""}
	
	tell (((zeroedNum as number) + 1) as text) to set newnum to text 1 thru (numlen - (length of it)) of zeroFill & it
	
	tell (a reference to text item delimiters) to set contents of it to tids
	
	return newnum
end incrementZ

Well we both have bugs in our solutions. While mine will break down when an real loses it’s precision (higher than 1E+22), yours will break down when integer are notated exponential (2 ^ 29 and higher). McUser’s example break when the initial question number is given, that’s how I find out.

Because the given number is an string the number can even exceed a reals presentation of a number. So therefore I post a third solution. This solution can contain unpronounceable figures but the drawback it’s also the slowest version. Just an example number that can’t be handled by AppleScript (real loses precision here) so My previous as McUser’s solution will break on the following number.

set myVar to "0033620340000000000000000000000000000000000000000000000000000000000000000001"
set newNum to ""
set sum to 1 --this will increment the value

repeat with symbol from (count myVar) to 1 by -1
	set sum to (character symbol of myVar) + sum
	set newNum to ((sum mod 10) as string) & newNum
	set sum to sum div 10
	if sum = 0 then
		if symbol is not 1 then set newNum to text 1 thru (symbol - 1) of myVar & newNum
		exit repeat
	end if
end repeat

if sum is 1 then
	set newNum to "1" & newNum
else
	repeat (length of myVar) - (length of newNum) times
		set newNum to "0" & newNum
	end repeat
end if
return newNum

edit: A more efficient way. When there is no remainder we can quit processing an simply copy everything from the left of our current symbol to the new number.

edit 2: A simplified version that doesn’t need any help from a list only remains in class string. This makes processing the number’s boundaries equal to the boundaries of the given value (length of the string).

Hello.

It was most interesting to read about the limitations. I really only assumed the integer to be within the magnitude 10^9.
( I really thought a real lost precision at 1E15, at least that was what it was the last time I investigated it.
Edit
And it still holds true. Maybe you are putting something else in the precisison than I do, because I mean how many digits you can safely assume are correct. :slight_smile:

(*
Accuracy: J is the number of significan bits in the mantissa of a real.
0.30103 is the number of significant digits in a decimal number. (The constant 0.30103 is log10 2
(Source: Meeus astronomical algorithms, first edition p. 17. rewritten from Basic by me.)
*)

set x to 1
set j to 0
repeat
	set x to x * 2
	if (x + 1) = x then exit repeat
	set j to j + 1
end repeat
log "" & (j * 0.30103) div 1

I have added a comment to my version, that makes the assumption about what range it is supposed to work within clear, and that the user can’t expect the solution to work for integers above 2^29.

Edit
The internal precisison actually holds for numbers up to 2^52 which is 4 503 599 627 370 496 (but the numbers are represented as reals from 2^29 onwards, (536 870 912 decimal) which is really the only sane way to treat such big numbers.) :slight_smile:

You’re right, i did something wrong from my 2 ^ 53 to 10E+n notation. Double (or float) can hold precise values until 2 ^ 53 which is a lot bigger than (2 ^ 29) -1.

Hello

Just for info.
I tried to run DJ Bazzie Wazzie’s script.

Alas, I got immediately this funny events log ( two different error numbers )

tell application “AppleScript Editor”
set sum to 2
→ error number -1728 from sum
Résultat :
error “Il est impossible de régler sum à 2.” number -10006 from sum

My understanding is that sum can’t be used as a variable name.
I guess that it’s due to Satimage which is available on my machine.

I liked the first script because it was possible to use it with an increment greater than 1 ( I tested with 19 )
At this time I didn’t find how edit the new one to do that.

Yvan KOENIG (VALLAURIS, France) lundi 10 mars 2014 20:52:06

I just cut it at 2^52, because that is the easiest limit to set up.

Now this was interesting, and if you really want to go higher, then you can by all means somehow do a mod (2^29) and div (2^29) to represent the parts as integers, (with leading zeroes), but when using numbers as big as that for representing series or models of stuff, or for implementing a kind of double Dewey system for that matter, I start to feel that it will be more appropriate to design a dedicated data structure for it, consisting of multiple integers, that logically represent “something”. :slight_smile:

Hi Guys,

thanks for all of your input and I must admit to getting lost in some of the maths, but I had an idea on the way home from work which I think will cover what I want to do simply:

set myVar to "0033620340000"
set leadingZeros to ""
repeat (count of characters of myVar) times
	set leadingZeros to leadingZeros & "0"
end repeat

repeat 15 times
	set tmpVar to do shell script "x=" & myVar & "; y=1; echo `expr $x + $y`"
	set myVar to text -(count of characters of myVar) thru -1 of (leadingZeros & tmpVar)
	display dialog myVar
end repeat

Please let me know your thoughts.

Thanks,
Nik

This seems similar to DJ’s second script, but is simpler:

set myVar to "0033620340000"
set incrementAmount to 1 -- Normally 1 for "increment", but larger integer values also work.

set myVar to incrementNumericStr(myVar, incrementAmount)

to incrementNumericStr(numStr, incrementAmount)
	set numStrDigits to numStr's characters
	
	set s to incrementAmount
	repeat with i from (count numStrDigits) to 1 by -1
		set s to s + (item i of numStrDigits)
		set item i of numStrDigits to s mod 10
		set s to s div 10
		if (s = 0) then exit repeat
	end repeat
	if (s > 0) then error "Overflow in incrementNumericStr(). Incrementing " & numStr & " by " & incrementAmount & " would increase the number of digits."
	
	set astid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to ""
	set newNumStr to numStrDigits as text
	set AppleScript's text item delimiters to astid
	
	return newNumStr
end incrementNumericStr

I always test my code on a clean machine, so I guess it is an scripting addition that causes the problem. Thanks for pointing it out.

I think, something I learned from C many years ago, that when you have an certain type of given value, you should stick what that sort of type object. I mean especially when it is with numbers. I think that the most know error caused by this is the cluster spacecraft launch that failed due to an integer overflow. You can even use 20 integers to allow higher values but it’s still limited while using an string and processing the string you are only limited to the same boundaries as the given string (it’s length). I know it’s a lot of processing and most will consider this as a lot of overhead but still banks today does process numbers this way.

We don’t know how big integer values get, you do know, but using a do shell script has the same boundaries as my first example. meaning that integer values become 2 ^ 53 (2 ^ 63 for bash) or larger it will lose it’s precision.

Hello.

First of all, let me say that when I wanted to break up the number, then it was because I assumed the number to be used in a series for catalouging something, and not say a “running-number” used to say; indexing a transaction.

As such, I saw that the number be used more as a symbolic number to denote, some kind of order, and then I felt, and still feel that it is better to use another datastructure than an integer to represent that “model-object”.

By the way: the Ariadne incident, (we should all be happy for the fact that there weren’t humans on board) due to using a library built for 32-bit on a 64 bit platform actually is something that I (like many others) have learned something from, when it comes to having old software run on a new platform.

Back to topic: I think Nigel’s solution is the best when done in AppleScript since he throws an error, I have just tried Nik’s expr, and that works with large integers, and is probably the best solution if the numbers that needs to be incremented is more likely to be above the 530 million magnitude, there is however a certain price to pay with regards to speed.

I haven’t tried this, but maybe the seq command are up for the job, and can deliver faster, but then again: you have to precompute the end value.

Why not make a composite solution Nik, where you implement your do shell script solution where Nigel’s throws an error?

Then you have the best of both worlds. :slight_smile:

Great suggestions guys. I’ll try and digest all of you points on a clear mind and come up with a final solution.

Thanks again for all of your help,
Nik

The main difference between Nigel’s version and mine is that I’ll make “99” + 1 just into “100” while Nigel’s version will throw an error. However Nigel’s script, like mine, can handle larger numbers than bash, so there is still gap in best of both worlds. To make Nigel’s script work like mine you can simply add a 1 to the beginning of the string when s is not 0 instead of throwing an error, but that only works when you increment the value by 1.

Do you know the reason? If you take a look at Nik’s script you’ll see that Nik doesn’t want the error to be thrown for that particular reason.

Hello.

It seems to me that expr handles numbers greater than 2^53:

Below is 2^53 + 5 * 10^9 (billions using the english name for it.)

It’s an 64 bit integer limitation but it’s still an limitation. But if the TS is okay with these boundaries, or if the given number is a 64 bit integer (which I doubt because it has leading zeros), then the problem is solved. Still I think it’s good to show a version like mine and Nigel that has no boundaries whatsoever, only the length of an string or list meaning the same boundaries as the given value type.