Applescript Repeat Loop Not Returning Expected Results - applescript

I am trying to "find-replace" a series of superscript cross-reference letters with numbers in Adobe InDesign. I have two "hard-coded" lists
set myLetters to {"a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a"}
set myNumbers to {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"}
It seems fairly straightforward but the script changes all of the occurrences to "1" on the first iteration and thus all subsequent replaces are ignored because the find is "false"
Any help and insight would be very much appreciated.
Here is the script as written:
tell application "Adobe InDesign 2023"
set myDocument to document 1
set include footnotes of find change text options to false
set include hidden layers of find change text options to false
set include locked layers for find of find change text options to false
set include locked stories for find of find change text options to false
set include master pages of find change text options to false
set whole word of find change text options to false
set case sensitive of find change text options to true
set search backwards of find change text options to false
set find text preferences to nothing
set change text preferences to nothing
set mySelection to selection
tell myDocument
set myCharacterStyle to object reference of character style "sup" of character style group "Cross References"
end tell
set myLetters to {"a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a"}
set myNumbers to {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"}
repeat with i from 1 to count of myLetters
set find what of find text preferences to item i of myLetters
set applied character style of find text preferences to myCharacterStyle
set change to of change text preferences to item i of myNumbers
change text mySelection
end repeat
end tell

Related

Generate a range of special characters with ruby

I'm very new to ruby at the moment but I came from a PHP background and must say that I enjoy doing ruby, alot. It's a really nice language and the community is strict but helpful.
Today I was looking at stackoverflow and checked one of my answers to a question to generate a random string using PHP. I had actually written a script for this so I thought, why not share it!
This script has some modifiers which allow you to choose wether you want to include the following sets
lowercase a-z
[1] + uppercase a-z
[1, 2] + numbers
[1, 2, 3] + special characters
[1, 2, 3, 4] + some crazy voodooh characters
So in this PHP script I physically typed each set into an array e.g.:
$charSubSets = array(
'abcdefghijklmnopqrstuvwxyz',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'0123456789',
'!##$%^&*()_+{}|:">?<[]\\\';,.`~',
'µñ©æáßðøäåé®þüúíóö'
);
and this was basically my way of being able to define complexity right there.
Now this looks alright, even in the code but ruby has ranges and ranges are something new and shiny for me to play with so I was thinking of building a random string generator later today just to get some more experience with it.
Now for my question, I know that you can do the following things with a range including:
'a'..'z'
'A'..'Z'
0..9
etc.. But I was thinking, could you also make a range with special characters? as in, only special characters? and if that is possible, would you also be able to do the same for the crazy voodooh?
The reason I'm asking is because there is no example in the docs or anything on SO explaining this.
Check out Range#to_a which is gotten from Enumerable. Note that on the left hand side of the docs it says that Range includes Enumerable, which means that the methods in Enumerable can be called on Ranges. If you can't find a method in a class, see what modules the docs say are included and click on the link to the included module.
Check out Array#shuffle.
Check out Array#join
Check out Array#[], which will take a range as a subscript, so you can take a slice of an array of random characters.
A two dot Range includes the end. A three dot Range doesn't include the end:
p (1...5).to_a #=> [1, 2, 3, 4]
Putting it all together:
chars = (0..9).to_a + ('A'..'z').to_a + ('!'..'?').to_a
10.times do
puts chars.shuffle[0..5].join
end
--output:--
I(m,E.
%_;i(3
rb=_ef
kJrA9n
YA`e.K
89qCji
Ba1x3D
acp)=8
2paq3I
U0>Znm
(Shakespeare will appear there eventually.)
Yes - this is certainly possible.
Fire up your console e.g. irb or pry.
1. for the special characters:
('!'..'?').to_a
# => [
# "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-",
# ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":",
# ";", "<", "=", ">", "?"
# ]
2. for the 'voodooh' characters:
('µ'..'ö').to_a
# => [
# "µ", "¶", "·", "¸", "¹", "º", "»", "¼", "½", "¾", "¿", "À", "Á",
# "Â", "Ã", "Ä", "Å", "Æ", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î",
# "Ï", "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö"
# ]
This is trivial to just try tho, the position (and kb index of the key) on your keyboard for the end special character defines what characters come inbetween, if I'd pick a ~ instead of a ? for the end it would look like this:
('!'..'~').to_a
# => [
# "`", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",",
# "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
# ":", ";", "<", "=", ">", "?", "#", "A", "B", "C", "D", "E", "F",
# "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S",
# "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_", "a",
# "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
# "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{",
# "|", "}", "~"
# ]
basically if character a is 65 and z is 90 then all characters inbetween like b which is 66 will be included, it works like that for anything you put in a range and since in ruby everything is an object, you can use anything in a range as long as it implements certain methods as explained by the docs!
EDIT (13-11-2015)
After doing some playing around in my console I came to this solution which "mimics" the given PHP example and perhaps even completes it.
def rng(length = 10, complexity = 4)
subsets = [("a".."z"), ("A".."Z"), (0..9), ("!".."?"), ("µ".."ö")]
chars = subsets[0..complexity].map { |subset| subset.to_a }.flatten
# => [
# "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l",
# "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x",
# "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
# "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
# "W", "X", "Y", "Z", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "!", "\"",
# "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".",
# "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":",
# ";", "<", "=", ">", "?", "µ", "¶", "·", "¸", "¹", "º", "»",
# "¼", "½", "¾", "¿", "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç",
# "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï", "Ð", "Ñ", "Ò", "Ó",
# "Ô", "Õ", "Ö"
# ]
chars.sample(length).join
end
Now calling rng will produce results like this:
rng # => "·boÇE»Ñ¼Á¸"
rng(10, 2) # => "nyLYAsxJi9"
rng(20, 2) # => "EOcQdjZa0t36xCN8TkoX"
EDIT#2 (14-05-2020)
As pointed out below in the comments, I did not even provide a documentation link to the relevant concept, in Ruby this is called a Range and can be found here (2.5.0).
If you need docs for your specific version, try googling for ruby range [your ruby version]. You can find out what your version is by running ruby -v in the terminal. Happy rubying :D
all dates are in dd-mm-yyyy format

How to access a hash that has a key that's an array and a value that's an integer

So I have a hash that looks like:
hash = { ["1", "2", "3"]=>"a", ["4", "5", "6"]=>"b", ["7", "8", "9"]=>"c" }
Though when I try to do something like hash[0] just a new line in my console shows up and if I try hash[0][0] it pops me an error that says [] method is undefined.
Now I'm wondering how to I access this in a way that I can do something like hash["1"] and it'll return me the "a".
I assume that since it lets me make hashes in this way I can access the content inside.
I'm not sure why you would want to create a hash with a key that's an array, but it works :)
hash = { ["1", "2", "3"]=>"a", ["4", "5", "6"]=>"b", ["7", "8", "9"]=>"c" }
hash[["1", "2", "3"]]
=> "a"
You might want to consider the opposite:
hash = { "a"=>["1", "2", "3"], "b"=>["4", "5", "6"], "c"=>["7", "8", "9"] }
hash["a"]
=> ["1", "2", "3"]
There's not a direct built-in way to access something like this, but by using select you can filter out the key/value pair that has the "1" and get the value for it:
hash.select { |key| key.include?("1") }.values.first
This assumes that each integer only exists in a single key.

What's the difference between string.split(",", -1) and string.split(",",-4) in ruby?

The ruby docs for the String class's split method state:
[If limit is] negative, there is no limit to the number of fields returned, and trailing null fields are not suppressed.
So as far as I can tell there is no difference between any of the following:
string="1,,2,3,,,4,,5,,6"
string.split(",", -1)
string.split(",", -4)
string.split(",", -1000000)
They all return the same value: ["1", "", "2", "3", "", "", "4", "", "5", "", "6"]
Just wondering why it would be possible to assign different negative limits in this way.
Also, what happens if I do want to remove the trailing null values (like whitespace)? I know I can do string.strip.split(",",-1) but is there a way to do it without using strip method?
By both your testing and your quoted documentation, there appears to be no difference between string.split(",", -1) and string.split(",", -4). The specification says there is no difference and you found no difference in testing. Hooray.
But note that string.strip.split(..) won't remove the trailing null values:
irb(main):003:0> string="1,,2,3,,,4,,5,,6,,,"
=> "1,,2,3,,,4,,5,,6,,,"
irb(main):004:0> string.split(",", -1)
=> ["1", "", "2", "3", "", "", "4", "", "5", "", "6", "", "", ""]
irb(main):005:0> string.strip.split(",", -1)
=> ["1", "", "2", "3", "", "", "4", "", "5", "", "6", "", "", ""]
You'll have to pick another mechanism to strip the nulls.
Ruby often has more than one way to do the same thing, even down to syntax. Just make a convention in your code to use -1.
Are you asking how to have no limit, but do the null field suppression? You can omit the limit parameter, per the docs.

Array Semi-Flattening

Want to convert this:
[["1", "2", "3"], ["4", "5", "6"]]
to this:
["1", "2", "3"], ["4", "5", "6"]
to be passed into Array.product(), and the first array can contain an unknown number of other arrays. for example, the array given may also be
[["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"]]
And ultimately, I need to pass the argument as:
otherArray.product(["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"])
Thanks ahead of time!
otherArray.product(*[["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"]]);
* is used in argument list to unpack array contents to arguments (like
here) or to pack arguments into an array,like in "def mymethod(*args)"
Reference: http://www.justskins.com/forums/apply-method-to-array-17387.html
I think what would work for you is using Ruby's Array expansion:
a=[[1,2,3],[4,5,6]]
b=[1,2,3].product([1,2,3],[4,5,6])
c=[1,2,3].product(*a)
b == c #This should be true
Basically putting the asterisk (*) in front of the variable will expand all elements in the array into a list of arguments, which is what you want.
The last line of code aside, the rest of it seems to be solved by using the 0 index:
arr[0]

Applescript: cleaning a string

I have this string that has illegal chars that I want to remove but I don't know what kind of chars may be present.
I built a list of chars that I want not to be filtered and I built this script (from another one I found on the web).
on clean_string(TheString)
--Store the current TIDs. To be polite to other scripts.
set previousDelimiter to AppleScript's text item delimiters
set potentialName to TheString
set legalName to {}
set legalCharacters to {"a", "b", "c", "d", "e", "f",
"g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r",
"s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E",
"F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
"S", "T", "U", "V", "W", "X", "Y", "Z", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "0", "?", "+", "-", "Ç", "ç", "á", "Á", "é",
"É", "í", "Í", "ó", "Ó", "ú", "Ú", "â", "Â", "ã", "Ã", "ñ", "Ñ",
"õ", "Õ", "à", "À", "è", "È", "ü", "Ü", "ö", "Ö", "!", "$", "%",
"/", "(", ")", "&", "€", "#", "#", "=", "*", "+", "-", ",", ".",
"–", "_", " ", ":", ";", ASCII character 10, ASCII character 13}
--Whatever you want to eliminate.
--Now iterate through the characters checking them.
repeat with thisCharacter in the characters of potentialName
set thisCharacter to thisCharacter as text
if thisCharacter is in legalCharacters then
set the end of legalName to thisCharacter
log (legalName as string)
end if
end repeat
--Make sure that you set the TIDs before making the
--list of characters into a string.
set AppleScript's text item delimiters to ""
--Check the name's length.
if length of legalName is greater than 32 then
set legalName to items 1 thru 32 of legalName as text
else
set legalName to legalName as text
end if
--Restore the current TIDs. To be polite to other scripts.
set AppleScript's text item delimiters to previousDelimiter
return legalName
end clean_string
The problem is that this script is slow as hell and gives me timeout.
What I am doing is checking character by character and comparing against the legalCharacters list. If the character is there, it is fine. If not, ignore.
Is there a fast way to do that?
something like
"look at every char of TheString and remove those that are not on legalCharacters"
?
thanks for any help.
What non-ascii characters are you running into? What is your file encoding?
It's much, much more efficient to use a shell script and tr, sed or perl to process text. All languages are installed by default in OS X.
You can use a shell script with tr (as the example below) to strip returns, and you can also use sed to strip spaces (not in the example below):
set clean_text to do shell script "echo " & quoted form of the_string & "| tr -d '\\r\\n' "
Technical Note TN2065: do shell script in AppleScript
Or, with perl, this will strip non-printing characters:
set x to quoted form of "Sample text. smdm#$%%&"
set y to do shell script "echo " & x & " | perl -pe 's/[^[:alnum:]|[:space:]]//g'"
Search around SO for other examples of using tr, sed and perl to process text with Applescript. Or search MacScripter / AppleScript | Forums
Another Shell script method might be:
set clean_text to do shell script "echo " & quoted form of the_string & "|sed \"s/[^[:alnum:][:space:]]//g\""
that uses sed to delete everything that isn't an alphanumeric character, or space. More regex reference here
Iterating in Applescript is always slow, and there really isn't a faster way around these problems. Logging in loops is an absolutely guaranteed way to slow things down. Use the log command judiciously.
In your specific case, however, you have a length limit, and moving the length check into into the repeat loop will potentially cut the processing time down considerably (just under a second to run in Script Debugger regardless of length of text):
on clean_string(TheString)
set potentialName to TheString
set legalName to {}
set legalCharacters to {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "?", "+", "-", "Ç", "ç", "á", "Á", "é", "É", "í", "Í", "ó", "Ó", "ú", "Ú", "â", "Â", "ã", "Ã", "ñ", "Ñ", "õ", "Õ", "à", "À", "è", "È", "ü", "Ü", "ö", "Ö", "!", "$", "%", "/", "(", ")", "&", "€", "#", "#", "=", "*", "+", "-", ",", ".", "–", "_", " ", ":", ";", ASCII character 10, ASCII character 13}
with timeout of 86400 seconds --86400 seconds = 24 hours
repeat with thisCharacter in the characters of potentialName
set thisCharacter to thisCharacter as text
if thisCharacter is in legalCharacters then
set the end of legalName to thisCharacter
if length of legalName is greater than 32 then
return legalName as text
end if
end if
end repeat
end timeout
return legalName as text
end clean_string
BBEdit or TextWrangler will be much, much faster at this. Download TextWrangler (it's free), then open up your file and run Text -> Zap Gremlins... on it. Does that do what you need? If it does, celebrate with a cold beverage. If not, try out BBEdit (it's not free) and create a new Text Factory with as many "Replace All" conditions as you need, then open up your file and run the Text Factory on it.

Resources