just would like to know, if there is a smarter way to insert hyphens into a string:
set PlainKey to "1234567890ABCDEFGHIJ" as string
set first_block to the text 1 thru 5 of PlainKey
set second_block to the text 6 thru 10 of PlainKey
set third_block to the text 11 thru 15 of PlainKey
set fourth_block to the text 16 thru 20 of PlainKey
set ActivationKey to first_block & "-" & second_block & "-" & third_block & "-" & fourth_block
set PlainKey to "1234567890ABCDEFGHIJ"
tell PlainKey
set ActivationKey to text 1 thru 5 & "-" & text 6 thru 10 & "-" & text 11 thru 15 & "-" & text 16 thru -1
end tell
Yvan KOENIG running El Capitan 10.11.3 in French (VALLAURIS, France) samedi 5 mars 2016 21:48:30
It seems like you want to add an hyphen after each 5 characters.
If you have AppleScript Toolbox installed and length of plainKey is unknown:
set PlainKey to "1234567890ABCDEFGHIJ"
set theItems to AST find regex ".{1,5}" in string PlainKey
joinItems(theItems, "-")
on joinItems(lst, sep)
tell AppleScript
set oldTIDs to text item delimiters
set text item delimiters to sep
set str to lst as string
set text item delimiters to oldTIDs
end tell
return str
end joinItems
if you don’t have AppleScript Toolbox installed and still have an variabe plainKey length:
set PlainKey to "1234567890ABCDEFGHIJ"
set theItems to separateByLength(PlainKey, 5)
joinItems(theItems, "-")
on separateByLength(str, len)
set lst to {}
repeat with i from (count of str) div len to 1 by -1
set pos to (((i - 1) * len) + 1)
set beginning of lst to text pos thru (pos + (len - 1)) of str
end repeat
if (count of str) mod len is not 0 then
set pos to (count of str) mod len * -1
set end of lst to text pos thru -1 of str
end if
return lst
end separateByLength
on joinItems(lst, sep)
tell AppleScript
set oldTIDs to text item delimiters
set text item delimiters to sep
set str to lst as string
set text item delimiters to oldTIDs
end tell
return str
end joinItems
Or if you have Satimage OSAX installed, you could use:
set PlainKey to "1234567890ABCDEFGHIJ"
set ActivationKey to (change "(.{5})(?!$)" into "\\1-" in PlainKey with regexp) -- Requires Satimage OSAX.
Or without any extras, there is of course sed:
set PlainKey to "1234567890ABCDEFGHIJ"
set ActivationKey to (do shell script ("<<<" & quoted form of PlainKey & " sed -E 's/(.{5})(.{5})(.{5})/\\1-\\2-\\3-/'"))
-- Alternatively:
set PlainKey to "1234567890ABCDEFGHIJ"
set ActivationKey to (do shell script ("<<<" & quoted form of PlainKey & " sed -E 's/.{5}/&-/g ; s/-$//'"))
I see you already have an answer. Here’s my solution that has more lines of code, but provides a general purpose handler that will handle any key size and block size.
After I wrote this, I saw DJ’s post, which is essentially the same approach.
Just for fun, I’ll go ahead an add mine to the collection:
set PlainKey to "1234567890ABCDEFGHIJ" as string
set ActivationKey to getActKey(PlainKey, 5, "-")
log ActivationKey
on getActKey(pPlainKey, pBlockSize, pDivStr)
### RETURNS STRING IN BLOCKS SEPARATED BY DIV STR ###
# IF pPlainKey is not evenly divisible by Block Size
# then the remaining characters are appended to end
set lenKey to length of pPlainKey
set numBlocks to lenKey div pBlockSize
set keyList to {}
--- BUILD LIST OF FIXED-SIZE KEY BLOCKS ---
set iStart to 1
repeat with iBlock from 1 to numBlocks
set end of keyList to text iStart thru (iStart + pBlockSize - 1) of pPlainKey
set iStart to iStart + pBlockSize
end repeat
--- CONVERT KEY LIST TO STRING ---
set AppleScript's text item delimiters to pDivStr
set actKey to keyList as text
--- APPEND REMAINING CHARACTERS (IF ANY) ---
set remainNum to lenKey mod pBlockSize
if remainNum > 0 then
set remainStr to text -remainNum thru -1 of pPlainKey
set actKey to actKey & pDivStr & remainStr
end if
return actKey
end getActKey
Thanks for adding to the contributions ” and for liking my Satimage script.
An alternative to your numBlocks/remainNum scheme would be to check for potential overrun during the repeat. This would be a trifle slower if the text had to be divided into hundreds or thousands of blocks, but wouldn’t make any difference here and would reduce the amount of code required.
set PlainKey to "1234567890ABCDEFGHIJ" as string
set ActivationKey to getActKey(PlainKey, 5, "-")
log ActivationKey
on getActKey(pPlainKey, pBlockSize, pDivStr)
### RETURNS STRING IN BLOCKS SEPARATED BY DIV STR ###
# IF pPlainKey is not evenly divisible by Block Size
# then the last block will be shorter.
set lenKey to length of pPlainKey
set keyList to {}
--- BUILD LIST OF KEY BLOCKS ---
repeat with iStart from 1 to lenKey by pBlockSize
set iEnd to iStart + pBlockSize - 1
if (iEnd > lenKey) then set iEnd to lenKey
set end of keyList to text iStart thru iEnd of pPlainKey
end repeat
--- CONVERT KEY LIST TO STRING ---
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to pDivStr
set actKey to keyList as text
set AppleScript's text item delimiters to astid
return actKey
end getActKey
As Nigel has mentioned, the code isn’t actually simplified or made more compact in runtime. Here an improved version with if-statement at the proper place: if no hyphen is needed then return string as original:
set PlainKey to "1234567890ABCDEFGHIJ" as string
set ActivationKey to getActKey(PlainKey, 5, "-")
on getActKey(pPlainKey, pBlockSize, pDivStr)
-- get length of argument because we need it multiple times
set lenKey to count pPlainKey
-- if we don't need to place any hyphens, return string
if lenKey ≤ pBlockSize then return pPlainKey
-- put the text items seperated by length in this list
set keyList to {}
-- only repeat when we need to place 1 or more pDivStr
-- this loop will get all the text items except the last one
repeat with iStart from 1 to (lenKey - pBlockSize) by pBlockSize
set end of keyList to text iStart thru (iStart + pBlockSize - 1) of pPlainKey
end repeat
-- the last text item is variable in length, therefore get it outside the loop.
set end of keyList to text (iStart + pBlockSize) thru -1 of pPlainKey
-- join the text items into a string and use pDivStr as separator
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to pDivStr
set actKey to keyList as text
set AppleScript's text item delimiters to astid
return actKey
end getActKey
IAC, when I said “That simplifies and compacts the code.”, I was referring to the text of Nigel’s code, in comparison with my code. IMO, his code is definitely simpler and more compact. Of course, you are entitled to your opinion.
Thanks for taking the time to review and to continue to improve my script (as improved by Nigel). Pulling the last block outside of the repeat loop is very clever.
So, can I get you guys to review and improve all my scripts?
Just kidding of course, but that would be awesome.
My version expends one integer comparison per block checking for the unlikely (but not impossible) circumstance that the lenth of pPlainKey isn’t an exact multiple of pBlockize. If the last block is indeed short, the existing value of one variable is shared with another variable. Since the number of blocks is expected to be about four and odd lengths are not expected to occur, this gives your repeat an advantage of four fewer integer operations overall.
Your repeat requires an additional integer calculation to set up, but this is counterbalanced by the need for one fewer operations in the calculation of the range for the last block.
However, your version also needs to perform an integer comparison to check for the vanishingly small (but not zero) possibility that pPlainKey’s length is shorter than the block length. This means that your version has an expected advantage over mine of just three fewer integer operations per complete run ” although since you’ve chosen to implement this check as a compound comparison instead of a simple comparison followed by an ‘else’ statement, the actual time advantage is probably only the equivalent of between two and three integer ops.
Both our versions could be “improved” by precalculatiing pBlockSize - 1, which would reduce my version’s workload by three integer operations and yours’s by two, leaving your version with an overall time advantage of between just one and two integer operations per entire run of the script. I rest my case, your honour.
I see you’ve swapped round the ‘if lenKey’ and ‘set keyList’ lines since last night. There may also be a minute (but likewise undetectable) speed advantage if you use lenKey instead of -1 in the range for the last block. The integer subtraction in the set-up for the repeat can be eliminated by looping with the range-end variable instead of with the range-start one ” although then you need check before adding any short last block:
set PlainKey to "1234567890ABCDEFGHIJ"
set ActivationKey to getActKey(PlainKey, 5, "-")
on getActKey(pPlainKey, pBlockSize, pDivStr)
set lenKey to count pPlainKey
set keyList to {}
set diff to pBlockSize - 1
repeat with iEnd from pBlockSize to lenKey by pBlockSize
set end of keyList to text (iEnd - diff) thru iEnd of pPlainKey
end repeat
set oddBlockLen to lenKey mod pBlockSize
if (oddBlockLen > 0) then set end of keyList to text -oddBlockLen thru lenKey of pPlainKey
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to pDivStr
set actKey to keyList as text
set AppleScript's text item delimiters to astid
return actKey
end getActKey
This is probably beating on a dead horse at this point, but I have two options that are somewhat different than above.
set {text item delimiters, theList, setoff} to {"-", {}, 5}
tell "1234567890ABCDEFGHIJ" to repeat with num from 1 to count by setoff
try
set theList's end to text num thru (text (num + (setoff - 1)))
on error --odd length
set theList's end to text num thru -1
end try
end repeat
theList as text
Possibly of interest to Nigel, as I used an alternative shell command/regex to his sed method:
set {theText, text item delimiters} to {"1234567890ABCDEFGHIJz", "-"}
(do shell script "ruby -e 'puts " & quote & theText & quote & ".scan /(.{5}|.+)/' ")'s paragraphs as text
I wasn’t aware of that. I know that that AppleScript will access each item in the list no matter which item your want[1]. I assumed that getting an item by positive or negative index have no difference in performance as a result. Thanks for pointing this out, even if the difference is minimal.
[1] There was an question in the AppleScript mailing list asking/pointing out that larger lists become slower even when you want the first or last item from it. AppleScript engineers told that AppleScript lists are actually vectors and not lists. The size of a vector doesn’t affect its speed which makes lists in AppleScript behave odd. The answer by AppleScript engineers (Chris) was that no matter which item you want from the list, the entire list needs to be counted and each item is validated before the item(s) is returned from it