How to set a variable to all the numbers located in a list?
Solution 1:
Objective: To process an AppleScript list of strings so that each string is filtered to remove any non-digit character followed by all leading zeroes. Resultant empty strings are discarded.
property digits : "0123456789"
property text item delimiters : {}
set L to {"abc", "qwerty.24", "01 abc23xyz", "123abc456", "2.1abc", "20"}
repeat with textItem in L
set stringOfDigits to filterCharacters from textItem thru digits
set numericalString to the processedNumericalString(stringOfDigits)
set the contents of the textItem to the numericalString
end repeat
return the strings in L --> {"24", "123", "123456", "21", "20"}
------------------------------------------------------------------------------------------
# HANDLERS:
to filterCharacters from someText as text thru allowedCharacters as text
local someText, allowedCharacters
set tid to my text item delimiters -- to reset them after
set my text item delimiters to {missing value} & characters in the allowedCharacters
set illegalCharacters to characters of (text items of someText as text)
set my text item delimiters to {missing value} & the illegalCharacters
set filteredText to the text items of someText as text
set my text item delimiters to tid
return the filteredText
end filterCharacters
on processedNumericalString(numericalString as text)
local numericalString
if the numericalString = "" then return missing value
try
the numericalString as number
on error
the numericalString
end try
return the result as text
end processedNumericalString
Breakdown
property
declarations
A property
is similar to a variable, except for two differences: properties are persistent, and have global scope; variables are disposed of when a script ends, and default to local scope. I will let you research those terms if you need to, as it's beyond the remit of this answer. I almost never use a property
for either of those features I just mentioned. Rather, I use them most often to separate storage of values that I generally intend to remain constant throughout the script (as opposed to variables that can be expected to change at any stage). AppleScript doesn't have a way to declare constants like other languages, so this is just a bastardisation of another construct.
Handlers
Handlers in AppleScript can be thought of to be what other languages refer to as a function, and the actual differences are nuanced and mostly academic, with one practical implication being that handlers aren't self-aware so can't report any information about themselves back to you. But, like functions, they receive arguments (or parameters), do something with them, and spit out a result at the end in most cases.
filterCharacters
: this is the core of the script in terms of where the heavy lifting is done. It receives a single piece of text you wish to be filtered, and a list of allowed characters, and removes everything else, i.e. it extracts occurrences of allowed characters in the order they occur. It does this by utilising AppleScript'stext item delimiters
, which you should read about more thoroughly, but essentially deletes all instances of delimiters in the list you supply it, splitting the string into fragments at the points of deletion. So, it's almost performing the opposite function that we want, and can be thought of being a text filter that you supply a list of illegals rather than a list of allowables. Therefore, I use them twice: first to strip out the allowables, leaving only the illegals; then giving that list of illegals back totext item delimiters
, which then get stripped from the original piece of text, thus leaving us only with allowables. Very efficient; avoids iterating through characters in the piece of text.processedNumericalString():
This handler is just a cheap and lazy way to strip off the leading zeroes once the first handler has given us a string containing only digits. It would be arguably more correct to iterate through each digit character until reaching the first non-zero character, deleting the ones that are, with a special case to prevent"0"
being obliterated. But, instead, I just decided to coerce thestring
to anumber
, then back to astring
. A hack that works effectively and one assumes more speedily too (though that's not definitive).
The main body of the script
With the two handlers defined at the bottom of the script, all that remains is to iterate through the list of strings, sending each string in turn to each handler. What comes back is the piece of text you desire, which set the contents of...
is what lets me replace the original item in the list with the new version. I was mindful not to retain empty strings (""
), and the processedNumericalString()
handler swaps those out for an AppleScript object called missing value
that represents exactly what it says (it's the closest pre-defined AppleScript object to what other languages often call null
or nil
). Anyway, the important this is it isn't a string
, because the final line of the script's run is:
return the strings in L
and this does exactly that: any item in the list that is not a string
(or text
) class of object is discarded. You can, of course, swap this line for:
set myVar to the strings in L
if you need it assigned to a variable in order to keep using it.
ADDENDUM
The OP added:
The list I described as
myVar
in the original post is always changing over time, I want the process of taking the items inmyVar
and isolating all the numbers to repeat indefinitely
OK. Let's bung the main part of the script into another handler:
to formNumericalStringsWithItems from L as list
local L
repeat with textItem in L
set stringOfDigits to filterCharacters from textItem thru digits
set numericalString to the processedNumericalString(stringOfDigits)
set the contents of the textItem to the numericalString
end repeat
return the strings in L
end formNumericalStringsWithItems
Place this with the other handlers to keep it separate and out of the way now. The main body of the script is reduced to these two lines, where I've used the identifier myVar
instead of L
just for clarity:
set myVar to {"abc", "qwerty.24", "01 abc23xyz", "123abc456", "2.1abc", "20"}
set myNewAndImprovedVar to formNumericalStringsWithItems from myVar
Now, wherever myVar
in your script gets updated/changed, you follow it with a call to this new handler, as I've illustrated here.
Just be clear about how I've delineated the different "parts" of the script: keep the property
declarations at the top—don't put them inside your repeat loop; also don't put the handler declarations inside the repeat loop—I put all of mine at the bottom of the script, where I've marked a line and headed a section only for handlers. The "main body" of the script (despite now only being two lines here) is what your code will occupy, so keep things neat and grouped (it's why I separate the property
declarations from the main body by two paragraph breaks instead of just one).