I have two lines in my address book's note field
Test 1
Test 2
I would like to get each line as a separate value or get the last line from the notes field.
I tried doing it this way:
tell application "Address Book"
set AppleScript's text item delimiters to "space"
get the note of person in group "Test Group"
end tell
but the result is
{"Test 1
Test 2"}
I'm looking for :
{"Test1","Test2"}
What am I doing incorrect?
There are a few things wrong with your code. First, you never actually ask for the text items of the note :-) You just get the raw string. The second is that set AppleScript's text item delimiters to "space" sets the text item delimiters to the literal string space. Thus, for instance, running
set AppleScript's text item delimiters to "space"
return text items of "THISspaceISspaceAspaceSTRING"
returns
{"THIS", "IS", "A", "STRING"}
Secondly, even if you had " " instead of "space", this would split your string on spaces, and not newlines. For instance, running
set AppleScript's text item delimiters to " "
return text items of "This is a string
which is on two lines."
returns
{"This", "is", "a", "string
which", "is", "on", "two", "lines."}
As you can see, "string\nwhich" is a single list item.
To do what you want, you can just use paragraphs of STRING; for instance, running
return paragraphs of "This is a string
which is on two lines."
returns the desired
{"This is a string", "which is on two lines."}
Now, I'm not entirely clear on exactly what you want to do. If you want to get this for a specific person, you can write
tell application "Address Book"
set n to the note of the first person whose name is "Antal S-Z"
return paragraphs of n
end tell
You have to split it into two statements because, I think, paragraphs of ... is a command, whereas everything on the first line is a property access. (I usually discover these things via trial and error, to be honest.)
If, on the other hand, you want to get this list for every person in a group, it's slightly harder. One big problem is that people without a note get missing value for their note, which isn't a string. If you want to ignore these people, then the following loop will work
tell application "Address Book"
set ns to {}
repeat with p in ¬
(every person in group "Test Group" whose note is not missing value)
set ns to ns & {paragraphs of (note of p as string)}
end repeat
return ns
end tell
The every person ... bit does exactly what it says, getting the relevant people; we then extract their note's paragraphs (after reminding AppleScript that the note of p really is a string). After this, ns will contain something like {{"Test 1", "Test 2"}, {"Test 3", "Test 4"}}.
Related
I would like to separate every URL in a text list with a comma in an Applescript application. An example input would be:
alloresto.fr eat.ch eatnow.com.au just-eat.ca just-eat.co.uk
Using Applescript's text item delimiters, I get partial success:
set enterDomains to myTextField's stringValue() as text -- gets text from text field in my xib window
set AppleScript's text item delimiters to ", "
set theResults to every word of enterDomains as string
set AppleScript's text item delimiters to ""
alloresto.fr, eat.ch, eatnow.com.au, just, eat.ca, just, eat.co.uk
But this breaks every domain with a - in it since I have it set to every word. Is it possible to ignore the - character when using Applescript's text item delimiters?
I know my issue is located with every word of enterDomains since hyphenated domains contain more than one word but when I change this line to something like text of enterDomains, it returns me the same list of domains as a result without any added commas.
Ideas or suggestions?
words of... versus text items of...
words of... functions independently of text item delimiters, so will always split a string in the same way.
text item delimiters allows you to specify one or more phrases at which to delimit a string to be separated into a list of text items. It also determines how a list of text items are joined together, so will be significant in any instance where a list object is coerced into text (or string) object.
To split a string at every occurrence of a space character, and only a space character:
set enterDomains to "alloresto.fr eat.ch eatnow.com.au just-eat.ca just-eat.co.uk"
set my text item delimiters to space
set theResults to text items in enterDomains
--> {"alloresto.fr", "eat.ch", "eatnow.com.au", "just-eat.ca", "just-eat.co.uk"}
Then, to join this list of text items into a string, delimited by a comma-space:
# ...Cont'd from the previous code block
set my text item delimiters to ", "
return theResults as text
--> "alloresto.fr, eat.ch, eatnow.com.au, just-eat.ca, just-eat.co.uk"
text item delimiters more generally
As I stated above, text item delimiters can actually be set to a list of several items, instead of just a single character or phrase. This causes a string to be split at every occurrence of every item in your specified list of text item delimiters, e.g.
set input to "The quick brown fox jumps over the lazy dog."
set my text item delimiters to {"i", space, "fox"}
get the text items of the input
--> {"The", "qu", "ck", "brown", "", "", "jumps", "over", "the", "lazy", "dog."}
The order of the delimiters does not matter here, because a string will be split using every delimiter acting in a simultaneous fashion.
However, when joining the list back up into a text object, only the first delimiter is used to glue the text items back together. In this case, it will be the "i":
# ...Cont'd from the previous code block
# result: {"The", "qu", "ck", "brown", "", "", "jumps", "over", "the", "lazy", "dog."}
return the result as text
--> "Theiquickibrowniiijumpsioveritheilazyidog."
Notice that when a string is split, every occurrence of a delimiter is deleted from the string; when it is joined, the first delimiter only is inserted in between each chunk of text. This is effectively replacing bits of text with something else.
Text replacement using text item delimiters (An introduction)
In your specific case, your task can be summarised as needing to replace the spaces in your string with comma-spaces. So we can do this in a single move, by setting the text item delimiters such that space characters are deleted, and ", " is inserted during concatenation:
set enterDomains to "alloresto.fr eat.ch eatnow.com.au just-eat.ca just-eat.co.uk"
set my text item delimiters to {", ", space}
return the text items of enterDomains as text
--> "alloresto.fr eat.ch, eatnow.com.au just-eat.ca, just-eat.co.uk"
There are other characteristics and peculiarities of text item delimiters that I've talked about in other answers on Stack Overflow, which you are free to search for. But, for the vast majority of use cases, the information above is the most relevant.
AppleScript-ObjC
...because I saw your use of stringValue() and reference to a .xib window, I'll quickly give you the AppleScriptObjC equivalent for some of the above scenarios.
As I'm sure you'll know, all of the examples will only work if the script in which they appear has the following initial lines of code:
use framework "Foundation"
# use scripting additions -- if Standard Additions are needed
property this : a reference to the current application
property NSArray : a reference to NSArray of this
property NSString : a reference to NSString of this
I'll use the assignment-declaration for enterDomains in your code as a starting point, but instead of coercing it to text, I'll leave it in as the cocoa referenced object returned by stringValue() (the -- comments out the coercion):
set enterDomains to myTextField's stringValue() -- as text
Therefore, enterDomains now contains an instance of an NSString class value object. So:
Splitting a string:
set theResults to enterDomains's componentsSeparatedByString:space
Joining a list of strings:
theResults's componentsJoinedByString:", "
Replacing every space with a comma-space:
my TextField's stringValue()'s stringByReplacingOccurrencesOfString:space withString:", "
return the result as text
See Apple's documentation on the NSString class for more stuff.
Consider this example:
set s to "alloresto.fr eat.ch eatnow.com.au just-eat.ca just-eat.co.uk"
set AppleScript's text item delimiters to " "
set theResults to every text item of s
-- {"alloresto.fr", "eat.ch", "eatnow.com.au", "just-eat.ca", "just-eat.co.uk"}
set AppleScript's text item delimiters to ""
Go ye and do likewise.
Look at the following code:
set TheStringsQ1Happy to {"Fabulous", "Great", "Alright", "Excited", "Not Bad", "", "Decent", "Fine", "Awesome", "Bored", "Cool", "Sad", "Fantastic", "Alright", "Good", "Ok"}
set theResponse to the text returned of (display dialog "" default answer "" giving up after 5)
if TheStringsQ1Happy contains theResponse then
display dialog "That's Great!"
else
say "That term is not in my vocabulary. Would you like me to add it?" using "Tom" speaking rate 220
set theResponseNotInVocabulary to text returned of (display dialog "" default answer "" giving up after 5)
if theResponseNotInVocabulary is "Yes" then
set end of TheStringsQ1Happy to theResponse
return TheStringsQ1Happy
end if
Although I can update TheStringsQ1Happy, this update only lasts the span of the script. How can I change the code so that every time I run the script, it also contains the updated vocabulary?
For example, if I said "All Good", the computer would recognize that the vocabulary is not on the list, and would later update this list only for that instance. How can I make it so that "All Good" stays for every instance from now on?
The following is strictly an example to help you with what you asked, not fix the broken code you posted.
If you run the following in Script Editor:
property theList : {1, 2, 3}
copy (count theList) + 1 to end of theList
log theList
You'll see theList as a property grow by 1 each time you run it, that is until the script is recompiled.
If you need absolute long term storage where nothing will be lost of anything added to theList, then you need to save to and retrieve from a disk file.
Variables in AppleScript don't span outside the duration of execution of the script in which they are defined, as you've quite rightly noticed.
However, a property can, and will continue into subsequent executions of a script with the information left over from the previous execution.
Bear in mind, though, that a property will get reset (restored to its original value) each time the script is re-compiled (which happens whenever you make edits to it, or trigger it to compile manually).
With this in mind, change this line:
set TheStringsQ1Happy to {"Fabulous", "Great", "Alright", "Excited", "Not Bad", "", "Decent", "Fine", "Awesome", "Bored", "Cool", "Sad", "Fantastic", "Alright", "Good", "Ok"}
to this:
property TheStringsQ1Happy : {"Fabulous", "Great", "Alright", "Excited", "Not Bad", "", "Decent", "Fine", "Awesome", "Bored", "Cool", "Sad", "Fantastic", "Alright", "Good", "Ok"}
and you'll be good to go.
If you want a more permanent way to ensure you don't accidentally lose the new additions to this property, such as when you need to make any changes to the script in the future that will reset its value, then you'll need to store the information in an external file, which will serve as a sort of "dictionary" of vocabulary terms.
The simplest way to do this is to create a text file and put each item of the list on its own line, like this:
Fabulous
Great
Alright
Excited
...etc.
Save it as something like HappyTerms.txt, somewhere like your Documents folder, then change the variable declaration for TheStringsQ1Happy to this:
set TheStringsQ1Happy to the paragraphs of (read "/Users/%you%/Documents/HappyTerms.txt")
replacing %you% with the name of your Home folder in which your Documents folder lives. In fact, it's a useful idea to put the path to this text file in its own variable definition just beforehand:
set VocabDictionary to "/Users/%you%/Documents/HappyTerms.txt"
set TheStringsQ1Happy to the paragraphs of (read VocabDictionary)
Finally, to make changes to this file and add new terms to it, immediately after this line:
if theResponseNotInVocabulary is "Yes" then set end of TheStringsQ1Happy to theResponse
simply add either these lines:
set AppleScript's text item delimiters to return
write (TheStringsQ1Happy as text) to VocabDictionary starting at 1
OR this line
write "\n" & theResponse to VocabDictionary starting at (get eof VocabDictionary) + 1
The first version overwrites the entire file with all the terms in the new list. The second version simply appends the new addition to the end of the file. You might want to experiment with both and get the file turning out the way you want it to, as you'll sneakily discover that one might give you a stray blank line in the file that results harmlessly in an empty string "" appearing in your list; whilst the other does not; but I'll leave you to figure out if and why this happens, and—should you really want it not to happen—how to prevent it. 🙃 Either way, it shouldn't cause you any problems.
If you have any other queries, post a comment and I'll back to you. If this is helpful, don't forget to +1 my answer, and mark it as "correct" if solves your problem for you.
This works for me using the latest version of Sierra
If this script is saved as an application,and you launch this new app... every time a new Item is added to the list, that new item will remain in the list every time you reopen the application. However if you open the application again in script editor,to edit the code and re-save... You will lose all of the values of the added list items And the whole cycle starts over again with the default list.
property TheStringsQ1Happy : {"Fabulous", "Great", "Alright", "Excited", "Not Bad", "", "Decent", "Fine", "Awesome", "Bored", "Cool", "Sad", "Fantastic", "Alright", "Good", "Ok"}
set theResponse to (display dialog "How Do You Feel Today?" default answer "" giving up after 25)
if text returned of theResponse is in TheStringsQ1Happy then
display dialog "That's Great!"
else
say "That term is not in my vocabulary. Would you like me to add it?" using "Tom" speaking rate 220
set theResponseNotInVocabulary to display dialog "Add This Term" & " " & quote & text returned of theResponse & quote & " " & "To My Vocabulary?" buttons {"No", "Yes"} default button 2
if the button returned of the result is "Yes" then
if TheStringsQ1Happy does not contain text returned of theResponse then
set end of TheStringsQ1Happy to text returned of theResponse
end if
return TheStringsQ1Happy
end if
end if
I'm new to AppleScript, but think it can automate a minor annoyance I sometimes deal with.
Lets say I write: "I like pizza, put bacon on it!"
But I decide I want to split that into two sentences: "I like pizza. Put bacon in it!"
I'd like to be able to select the string ", p"
And with a keyboard shortcut, have a script remove any punctuation, add a period, and capitalize the first letter.
I figured out how to set System Preferences > Keyboard to run an automator service, running an AppleScript, but I can't google enough info to create the script from scratch.
Any help or direction would be great!
Alternative: Since Automator supports other interpreters too, there is no strict need to use AppleScript.
Here's an alternative solution that uses a Run Shell Script action to combine bash with awk, which makes for much less code:
Create an Automator service that:
receives selected text in any application
has check box Output replaces selected text checked
contains a Run Shell Script action with the code below
and then invoke the service with the entire sentence ("I like pizza, put bacon on it!") selected.
awk -F ', ' '{
printf $1 # print the first "field"; lines without ", " only have 1 field
for (i=2; i<=NF; ++i) { # all other fields, i.e., all ", "-separated clauses.
# Join the remaining clauses with ". " with their 1st char. capitalized.
printf ". " toupper(substr($i,1,1)) substr($i,2)
}
printf "\n" # terminate the output with a newline
}'
Caveat: incredibly, awk (as of OS X 10.9.2) doesn't process foreign characters correctly - passing them through unmodified works, but toupper() and tolower() do not recognize them as letters. Thus, this solution will not correctly work for input where a foreign char. must be uppercased; e.g.: "I like pizza, ṕut bacon on it!" will NOT convert the ṕ to Ṕ.
If you:
create an Automator service that:
receives selected text in any application
has check box Output replaces selected text checked
contains a Run AppleScript action with the code below
and then invoke the service with the entire sentence ("I like pizza, put bacon on it!") selected
it should do what you want.
on run {input, parameters}
# Initialize return variable.
set res to ""
# Split the selection into clauses by ", ".
set {orgTIDs, AppleScript's text item delimiters} to {AppleScript's text item delimiters, {", "}} #'
set clauses to text items of item 1 of input
set AppleScript's text item delimiters to orgTIDs #'
# Join the clauses with ". ", capitalizing the first letter of all clauses but the first.
repeat with clause in clauses
if res = "" then
set res to clause
else
set res to res & ". " & my capitalizeFirstChar(clause)
end if
end repeat
# Return the result
return res
end run
# Returns the specified string with its first character capitalized.
on capitalizeFirstChar(s)
set {orgTIDs, AppleScript's text item delimiters} to {AppleScript's text item delimiters, {""}} #'
set res to my toUpper((text item 1 of s) as text) & text items 2 thru -1 of s as text
set AppleScript's text item delimiters to orgTIDs #'
return res
end capitalizeFirstChar
# Converts the specified string (as a whole) to uppercase.
on toUpper(s)
tell AppleScript to return do shell script "export LANG='" & user locale of (system info) & ".UTF-8'; tr [:lower:] [:upper:] <<< " & quoted form of s
end toUpper
Is there a way, say using a space char as the string delimiter, set a string to the first three words of a paragraph including spaces.
For example
set a to "This is my test string"
set b to words 1 thru to 3 of a
set c to words 1 thru to 3 of a as rich text
return {b,c}
Returns {{"This","is","my"},"Thisismy"}
I want to set a variable so in this case of a, it would be set to "This is my".
First let's explain what happens. words 1 thru 3 of a as rich text is getting a range of words as an list. Then as rich text (which should be as string) coerces the list into an string. When you coerce an list (or record) to an string AppleScript will use an separator called text item delimiter. By default it is set to "". This means there is no separator (delimiter) used and the words are glued together. But let's see what happens when we set temporarily the text item delimiters to space.
set a to "This is my test string"
set b to words 1 thru 3 of a
set {oldTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, space}
set c to words 1 thru 3 of a as string
set AppleScript's text item delimiters to oldTID
return {b, c}
now it returns {{"This", "is", "my"}, "This is my"}
I have the following working example AppleScript snippet:
set str to "This is a string"
set outlist to {}
repeat with wrd in words of str
if wrd contains "is" then set end of outlist to wrd
end repeat
I know the whose clause in AppleScript can often be used to replace repeat loops such as this to significant performance gain. However in the case of text element lists such as words, characters and paragraphs I haven't been able to figure out a way to make this work.
I have tried:
set outlist to words of str whose text contains "is"
This fails with:
error "Can’t get {\"This\", \"is\", \"a\", \"string\"} whose text contains \"is\"." number -1728
, presumably because "text" is not a property of the text class. Looking at the AppleScript Reference for the text class, I see that "quoted form" is a property of the text class, so I half expected this to work:
set outlist to words of str whose quoted form contains "is"
But this also fails, with:
error "Can’t get {\"This\", \"is\", \"a\", \"string\"} whose quoted form contains \"is\"." number -1728
Is there any way to replace such a repeat loop with a whose clause in AppleScript?
From page 534 (working with text) of AppleScript 1-2-3
AppleScript does not consider paragraphs, words, and characters to be
scriptable objects that can be located by using the values of their
properties or elements in searches using a filter reference, or whose
clause.
Here is another approach:
set str to "This is a string"
set outlist to paragraphs of (do shell script "grep -o '\\w*is\\w*' <<< " & quoted form of str)
As #adayzdone has shown. It looks like you are out of luck with that.
But you could try using the offset command like this.
set wrd to "I am here"
set outlist to {}
set str to " This is a word"
if ((offset of space & "is" & space in str) as integer) is greater than 0 then set end of outlist to wrd
Note the spaces around "is" . This makes sure Offset is finding a whole word. Offset will find the first matching "is" in "This" otherwise.
UPDATE.
To use it as the OP wants
set wrd to "I am here"
set outlist to {}
set str to " This is a word"
repeat with wrd in words of str
if ((offset of "is" in wrd) as integer) is greater than 0 then set end of outlist to (wrd as string)
end repeat
-->{"This", "is"}