I just discovered this very cool, compact script by @ShaneStanley, and just wanted to share it with you guys.
Thanks for a great script, Shane.
(*
REFERENCE:
Topic Title: How do I convert a string of numbers from text to a list if integers?
URL: http://macscripter.netviewtopic.php?pid=169183#p169183
Date: 2013-11-28
Author: Shane Stanley
Shane: "It's a lot shorter, but takes a lot longer to run."
Note: Script slightly modified by @JMichaelTX to add a bit of clarity.
*)
--- It seems very fast to me in 2017-06-08, running Script Debugger 6.0.4 (6A198) on macOS 10.11.6 --
set stringOfNumbers to "1, -2, 3.1234, 4, -95.123"
set listOfNumbers to run script ("{" & stringOfNumbers & "}")
-->{1, -2, 3.1234, 4, -95.123}
I don’t know that I’d call any script that resorts to run script as cool – to me, it has a vaguely dirty feel about it. OTOH, in this particular case, it may be a good choice.
But I suspect that in the real world, most of the time the string will contain all reals or all integers, not a mixture of both. In those cases, using text item delimiters and coercing is really much nicer code IMO, as well as faster – and not particularly long.
Alternatively, and faster with longer lists:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
set stringOfNumbers to "1, -2, 3.1234, 4, -95.123"
set stringOfNumbers to current application's NSString's stringWithString:stringOfNumbers
set listOfNumbers to ((stringOfNumbers's componentsSeparatedByString:",")'s valueForKey:"doubleValue") as list
Including for script timing. Script Debugger’s times are indicative, but the nature of a script editor means they can only ever be very rough. Try something like Script Geek:
It’s still not perfect – timing for AppleScript is always dependent on the host – but it eliminates a lot of overhead. Using it and the ASObjC method with a list of 40 entries, I time 100 iterations at 0.031 seconds. That’s about 0.0003 per iteration.
I tested both scripts using your Script Geek. I compiled each and then ran for 10 times:
I then ran both again (for 10 trials), and got identical results:
So, my conclusion has not changed. At least for small lists, there is no penalty for using the original script that used run script, and there may be a small performance advantage. The run script method also offers a very compact solution, and works with both integers and reals in the same list.
So both us dealt with a list in which all of its items could be converted to a number.
But what about the case of mixed list, containing letters, integers, reals, as in: {"Some alpha text", "1", "3.14"}
Here’s the best I can do:
set myList to {"Some alpha text", "1", "3.14"}
repeat with oItem in myList
set contents of oItem to my textToNum(contents of oItem)
end repeat
on textToNum(pString)
set numOrText to pString
try
set numOrText to numOrText as number
on error errMsg number errNum
set x to numOrText
end try
return numOrText
end textToNum
--> {"Some alpha text", 1, 3.14}
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
set myList to {"Some alpha text", "1", "-3.14"}
set myArray to current application's class "NSMutableArray"'s arrayWithArray:(myList)
repeat with thisString in myArray
if ((thisString's rangeOfString:("^[0-9.-]++$") options:(current application's NSRegularExpressionSearch))'s |length|() > 0) then set thisString's contents to thisString as text as number
end repeat
set myList to myArray as list
Or, performing the changes in the original list:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
set myList to {"Some alpha text", "1", "-3.14"}
set myArray to current application's class "NSArray"'s arrayWithArray:(myList)
repeat with i from 1 to (count myList)
if (((myArray's item i)'s rangeOfString:("^[0-9.-]++$") options:(current application's NSRegularExpressionSearch))'s |length|() > 0) then set myList's item i to myList's item i as number
end repeat
return myList
And here’s a minor performance tweak to the original:
on textToNum(pString)
try
if "0123456789-." contains character 1 of pString then
set pString to pString as number
end if
end try
return pString
end textToNum
Ah, that is a slick solution to the one issue that arises with the original and the way it coerces an empty string to 0.
My obvious response was to add a line to cater for this exceptional case, but it’s really pleasing when a streamlined solution also covers all the bases.
EDIT: Your version doesn’t cater for a numerical string with leading white space.
Thanks to everyone who responded to this question.
Based on everyone’s input, here is my revised script, which seems to work very fast and handle all cases.
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
on textToNum(pString)
--–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
(* VER: 1.1 2018-06-17
PURPOSE:
PARAMETERS:
• pString | text | Source String
RETURNS: | number or text or missing value |
• Number IF string can be converted;
• Else source string unchanged.
• missing value IF empty pString
AUTHOR: JMichaelTX
--–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
*)
if (length of pString > 0) then
try
set numOrText to pString as number
on error -- errMsg number errNum
set numOrText to pString
end try
else -- IF pString is empty
set numOrText to missing value
end if
return numOrText
end textToNum
--~~~~~~~~~~~~~~~~~~~~ END of Handler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
My comments where more in the context of using run script to avoid a bit of coding — your case is quite different.
It seems to me that we now have a sanctioned library scheme with use script, and although not perfect, it works well enough and is pretty flexible — no more stuff like hard-coding paths. It works fine for me. But then I don’t care that they’re not .applescript files, and I actually prefer my code to give an indication of where handlers come from.
Am not sure if this adds anything of value to the conversation, but the following handler will load a script library regardless of the AS format in which it’s saved. No hard-coded path is needed, but the library file must be located in a valid “Script Libraries” folder and use the appropriate file extension for its format.
Stan C.
p.s. — If anyone is able extract the library’s path from the “on error” handler without using «class seld» of (offendingObject as record), I’d love to see how they did it.
set myLib1 to loadLibrary("myLib.app")
set myLib2 to loadLibrary("myLib.scpt")
set myLib3 to loadLibrary("myLib.scptd")
set myLib4 to loadLibrary("myLib.applescript")
on loadLibrary(libName)
try
-- succeeds if library is compiled (.app, .scpt, or .scptd file)
set theLib to script libName
on error errText number errNum from offendingObject
if errText contains "doesn’t seem to belong" then
-- library is saved as text (.applescript file)
set libPath to «class seld» of (offendingObject as record) -- extract path
set theLib to run script ("script s" & return & ¬
(read file libPath as «class utf8») & return & ¬
"end script " & return & "return s")
else -- unexpected error, so throw it
error errText number errNum from offendingObject
end if
end try
return theLib
end loadLibrary
In High Sierra (Edit: and El Capitan), you can coerce offendingObject directly to «class furl» or to alias:
on loadLibrary(libName)
try
-- succeeds if library is compiled (.app, .scpt, or .scptd file)
set theLib to script libName
on error errText number errNum from offendingObject
if errText contains "doesn’t seem to belong" then
-- library is saved as text (.applescript file)
set theLib to run script ("script s" & return & ¬
(read (offendingObject as «class furl») as «class utf8») & return & ¬
"end script " & return & "return s")
else -- unexpected error, so throw it
error errText number errNum from offendingObject
end if
end try
return theLib
end loadLibrary
Yes, both of those methods work here, since I’m running El Capitan. I’ve now upgraded the handler to your new version, which uses «class furl». Many thanks for the helpful insights!