If you tend to accumulate a large number of scripts and script snippets in your Scripts folder, you probably face this frustration. When tackling a new problem, you know that you wrote (or copied from a thread you saw in macscripter.net) a handler that will do just the trick required, but unfortunately, it is buried in some other script in the collection in your Scripts folder. Not wanting to re-invent the wheel, you end up Spotlighting for the lost handler, the name of which you have of course forgotten, and when you find it, you copy and paste it into your new script and modify its arguments for your new problem. Not very efficient. Unfortunately, I do it all the time.
There is an easy way to avoid this repeated search pattern, however, that requires only a modicum of self-discipline: build yourself a library of your favorite handlers in several categories and use them in new scripts by simply referring to them in your library. If you want to share the script, you can always go and get it from the library. I’ve just started to do this, and wish I had started long ago. This tutorial illustrates how to start, and necessarily leaves out a lot of what you can do with libraries.
What is a Library?
Virtually any script can be included in another by “inhaling” it with the following instructions:
property MyLibPath : ((path to scripts folder from user domain) & "Script Library:") as text
property LibSort : load script (MyLibPath & "MultiSort.scpt") as alias
Now lets suppose that MultiSort.scpt contains (if reasonably well named) nothing but handlers for sorting a list, e.g., to BubbleS, to SimpleS, to QuickS, etc. (There are good examples of high-speed sort handlers in in this thread in Code Exchange which is part of the bbs referred to above.) Then assuming that we had included the two lines of AppleScript above early in our script, we could refer to any of our sorters in a new script like this:
set NewList to LibSort's BubbleS(ListToSort)
This would have exactly the same effect as copying that handler from MultiSort.scpt and pasting it into our new one, without actually doing that directly, though we have, of course, done it indirectly. This can be a problem sometimes because AppleScript remembers variables and your library can change them - it’s part of your script.
Some Examples
As a simple example of this, suppose that we often had to sort two lists on the basis of the content of one of them while keeping the other in the same order, sometimes wanted to randomize a list or two, and remove items from one or two lists. We can put all of those handlers into one script, save it in our library, and recover it by reference in any other script. The last script in this tutorial is a demo library of handlers; I’ve put it there so it will not occupy the entire first screen of this tutorial.
You should open that script in your default script editor by clicking on the link > Open this Scriptlet in your Editor: at the head of that script, and then save it to a new folder in your user’s Scripts folder. The folder should be called: “Script Library”. If you already have one, fine; just save the script above (as a script) in it, and call it “MultiLists”.
Library scripts can contain handlers that call others in the same script with the usual caveat of remembering to use my in the reference to them. Look at these now. The last of the handlers is a generic one for removing an item in a given list, and the last just calls it twice since there is no problem in keeping the order of the removal in two (or more) lists.
Because we’re absolutely certain to forget how to use these scripts later, I’ve included a first handler that does nothing more than provide the text for a “help” dialog. After you’ve moved the library file as instructed above, run this:
property Lib : (path to scripts folder from user domain) & "Script Library:"
property myLib : load script ((Lib as text) & "MultiLists.scpt") as alias
display dialog myLib's helpMe()
Looking further at MultiLists, you might ask why we need sort2()?. Suppose you had extracted a bunch of event summaries and their dates in two lists in a tell application “iCal” block like the one below, and you wanted to sort them by date while keeping the the corresponding events corresponding with their dates. iCal doesn’t keep them that way - they’re in the order entered, and only one copy of a repeated item is kept, so a double Sort does that.
-- Sample iCal script that generates two lists.
-- Won't work for most readers. my iCal is set up for it. - just illustrative.
set today to (current date)
tell application "iCal"
close window 1 -- don't need to watch it work and telling opens it.
set MCals to every calendar whose title contains "Medical" -- a list
set mCal to item 1 of MCals -- the first is the only in this list.
-- Collect the date/name list as mdCal
set mCount to count events of mCal
set mdEvent to {} -- placeholder for events
set mdDate to {} -- placeholder for their dates
repeat with n from 1 to mCount -- run through the whole calendar
-- Get [i]summary[/i] and [i]start date[/i] of each
tell event n of mCal
set {anEvent, aDate} to {summary of it, start date of it}
-- Figure in repetitive events by weeks, adjusting forward to today.
-- This is why we had to review all events - repeats are not listed again.
if anEvent contains "(" then -- in my Medical calendar only repeats do.
set paren to offset of "(" in anEvent
set Rep to character (paren + 1) of anEvent -- assumes less than 10
-- The next line counts forward from the original to one after today.
tell (Rep * weeks) to set aDate to today + (it - ((today - aDate) mod it))
end if
end tell
-- Now collect those after today.
if ((aDate) - today) > 0 then -- dates can only be added or subtracted, not A > B.
set end of mdEvent to anEvent -- a list of medical appointments coming up
set end of mdDate to aDate -- a list of dates in the same order as the appointments
end if
end repeat
end tell
So, if we had done this and got back the lists mdEvent and mdDate lists, then we could sort them by date like this:
property tLib : (path to scripts folder from user domain) & "Script Library:"
proprty sorter : load script ((Lib as text) & "MultiLists.scpt") as alias
set {mdDate, mdEvent} to sorter's sort2(mdDate, MdEvent) -- done.
More Examples of Our Simple Library Handlers
Let’s use a simpler example to illustrate how to use our MultiLists Library:
-- load it into this script....
property Lib : (path to scripts folder from user domain as text) & "Script Library:"
property LL : load script (Lib & "MultiLists.scpt") as alias
-- And two Lists...
property Lst1 : {15, 7, 5, 2}
property Lst2 : {"Fifteenth", "Seventh", "Fifth", "Second"}
LL's helpMe()
copy {Lst1, Lst2} to {L1, L2}
set double to LL's sort2(L1, L2) -- sorting on the first list
copy Lst2 to RL
set RList to LL's RandList(RL) -- randomizing a list
copy {Lst1, Lst2} to {R1L, R2L}
set r2List to LL's RandList2(R1L, R2L) -- randomizing two
copy Lst2 to OG
set OneGone to LL's removeListItem(2, OG) -- remove an item
copy {Lst1, Lst2} to {TG1, TG2}
set TwoGone to LL's removeListItem2(3, TG1, TG2) -- remove one from two
set blank to return & "------" & return
set msg to "Given the original lists:" & return & makeText(Lst1) & return & makeText(Lst2) & blank & "The double sort returned" & return & makeText(item 1 of double) & return & makeText(item 2 of double) & blank & "The randomized single list returned" & return & makeText(RList) & blank & "The randomized double list is" & return & makeText(item 1 of r2List) & return & makeText(item 2 of r2List) & blank & "With one item in the second list gone:" & return & makeText(OneGone) & blank & "Finally, with an item removed in two" & return & makeText(item 1 of TwoGone) & return & makeText(item 2 of TwoGone) & blank
display dialog msg buttons {"Done"} default button 1
to makeText(aList)
set text item delimiters to ", "
set L to aList as text
set text item delimiters to ""
return "{" & L & "}"
end makeText
Now, if I could only organize myself suffiently to comb through my scripts, extract families of handlers, and build a huge library of them so I could stop hunting.
The Collection of List Handlers to become ~/Library/Scripts/Script Library/MultiLists.scpt
------ This handler simply returns a text message of instructions.
to helpMe()
set msg to "MULTILISTS' HELP
-----
This demo Library sorts two lists, removes an
item from a list, randomizes the items in
a list, & randomizes one while keeping a
second properly aligned to the first.
-----
sort2(s1, s2) ASCII (or Date) sorts the
contents of list s1 while keeping the
contents of list s2 in register with
those in list s1.
-----
RandList() accepts a single list as its argument
and returns the list in randomized order.
-----
RandList2 does the same as RandList, but keeps
the two lists in lockstep order: RandList2(L1, L2).
-----
RemoveListItem(N,L) removes the Nth item
from the list L, while RemoveListItem2() uses
RemoveListItem twice in a coordinated manner."
display dialog msg
end helpMe
-------
-- Sort2 is an adaptation of a script by Kai Edwards
-------
to sort2(s1, s2)
script M
property srt : missing value
property sec : missing value
end script
set M's srt to s1
set M's sec to s2
tell (count M's srt) to repeat with i from (it - 1) to 1 by -1
set s to (M's srt)'s item i
set r to (M's sec)'s item i
repeat with i from (i + 1) to it
tell (M's srt)'s item i to if s > it then
set (M's srt)'s item (i - 1) to it
set (M's sec)'s item (i - 1) to (M's sec)'s item i
else
set (M's srt)'s item (i - 1) to s
set (M's sec)'s item (i - 1) to r
exit repeat
end if
end repeat
if it is i and s > (M's srt)'s end then
set (M's srt)'s item it to s
set (M's sec)'s item it to r
end if
end repeat
return {M's srt, M's sec}
end sort2
------ This handler randomizes a list, relocating its items in
------ random order. Some may end up in the same position.
to RandList(aList)
set c to count aList -- so as not to calculate it every loop
set r to {} -- placeholder for the answer
repeat until (count r) = c
tell (some item of aList) to if (it is not in r) then set end of r to it
end repeat
return r
end RandList
------ This handler randomizes one list and keeps the second in the
------ same random order as the first.
to RandList2(L1, L2)
set c to count L1 -- so as not to calculate it in every repeat
set k to {} -- place holder for index
set r to {} -- placeholder for randomized List1
set s to {} -- placeholder for randomized List2
repeat until (count k) = c
tell (random number from 1 to c) as integer to if it is not in k then set end of k to it
end repeat
repeat with n from 1 to c
tell item n of k
set end of r to item it of L1
set end of s to item it of L2
end tell
end repeat
return {r, s}
end RandList2
------
-- From a script by Matt Neuburg
-- This handler removes an item (by item number from a list
to removeListItem(anItemNum, aList)
if aList is {} then
return {}
else if anItemNum is 1 then
return rest of aList
else
return {item 1 of aList} & removeListItem(anItemNum - 1, rest of aList)
end if
end removeListItem
------
-- This handler removes the same item number from two lists
to removeListItem2(anItemNum, aList, bList)
set L1 to my removeListItem(anItemNum, aList)
set L2 to my removeListItem(anItemNum, bList)
return {L1, L2}
end removeListItem2