AppleScript for Beginners III - The Power of Lists

Welcome back to the show. At the end of our last episode, I left you with this little script to play around with and try to understand:

tell application "iTunes"
	set all_Playlist_Names to the name of every playlist
end tell

When you ran it, you should have seen results something like this:

{“Library”, “Party Shuffle”, “Purchased”, “Television”, “Top 25 Most Played”,“Podcasts”, “Sundays”, “Voice”}

Of course, it all depends upon how you have organized your version of iTunes, and what sorts of playlists you have there, and how you have named them. Today, we are going to focus on the type of result you have obtained, rather than the details themselves. Whenever your result has the little curly braces ({ and }) on each end, it is a list.

Lists are by far the most powerful tools that you have to work with as an AppleScripter, and are really what gives AppleScript the ability to do all sorts of repetitive tasks for you. There are also an abundance of built-in devices to work with lists, allowing you nearly infinite control over the data you generate in a list, or even how you produce that list in the first place.

A list can hold any kind of data you want. You can have a list of numbers, a list of strings, a list of references to something (we’ll do this later in the program), a list of lists, or even a mixture of all these things. One term you need to remember about lists is the term item. An item is a single unit of a list. If your list contains numbers, then any single number of that list is an item. If your list contains other lists, any one list inside your list is also an item.

Here are some examples of lists:

--This list contains only numbers
set number_List to {2, 4, 5, 2, 32, 564, 7565, 3, 1, 0, -5}
--This list contains lists of both numbers and strings
set list_List to {{1, 2, 3}, {"one", "two", "three"}, {1, "two", 3}, {4, 5, "six", "7"}}
set mixed_List to {65, 45, 23, {14, "Hello", "Mr.", "Goodbar"}, "more", "strings"}--This list contains numbers, lists, and strings

See how I have included some descriptive terms above the first two lists? These are comments, and are invaluable when putting scripts together either as a beginner, or an advanced programmer. Whenever you want to remember something about a part of a script, or if you just want to take notes, simply type two hyphens right next to each other (–), and every thing after that is a comment. Sometimes, it may be weeks, or longer between sessions working on a particular script. By keeping notes all along, it will be easier to get back up to speed when you return to your project. You can put comments on their own lines, or at the end of a line. Start using them today.

Let’s get back to our discussion of lists, referring again to your list of playlist names. When you run that script, iTunes generates a list of the names of all your playlists. What you need to understand here is that what you get is a list of strings, NOT a list of your playlists. Yes, these strings all represent the names of your playlists, but they are now just strings. (To use them in order to get at your playlists will be introduced shortly.) OK, so what? What can you do with this list? Here are some things to play around with, and see what AppleScript can do with lists:

(Place each of these lines at the end of the script, one at a time, just after the ‘end tell’)

count all_Playlist_Names

This will tell you how many items are in your list.

beginning of all_Playlist_Names

This will give you the first item of the list.

end of all_Playlist_Names

This will give you the last item in the list.

You can also reference items in your list by number, using positive or negative numbers. For instance,

item -2 of all_Playlist_Names

will return the second to last item of your list. What if you want the middle item of the list? No problem:

set playlist_Middle to middle item of all_Playlist_Names

What do you get if the list has an even number of items like 4 for example? You get the second one, the one just to the left of center. The list object referred to by “middle” doesn’t have to be a generic “item” either:

set L to {1, "A", "B", 4, {"j", 8, "k"}, 5, "C", 6, 7}
set m to middle number of L --> 5
set t to middle text of L --> "B"
set p to middle list of L --> {"j", 8, "k"}

Another way to access a list’s data is by using an apostrophe instead of the term ‘of’, like this:

set playlist_Middle to all_Playlist_Names's middle item

Let us now switch gears a little bit, and delve more into both lists and iTunes. Strap on your safety belt, this could get a bit wild. We know how to get a list of playlists, but what if we want a list of tracks inside of a playlist? (A track is what iTunes calls a song. Get used to it.) Well, first you need to know which playlist you want. For now, let’s build on our little script that gets the playlist name in the middle of the list of all playlist names. We’ll just see what tracks are in there. First, as you expected, we need to set the name of that playlist to a variable, then go get the tracks, in much the same fashion that we got the playlists originally. You need to delete the last line of the script, and use these lines after the ‘end tell’:

set playlist_Middle to all_Playlist_Names's middle item
tell application "iTunes" to set all_Middle_tracks to the name of every track of playlist playlist_Middle

YIKES! It works, but it is pretty confusing, isn’t it? Stop hyperventilating, and let’s see what we can learn from this. The first line is easy; we have made a new variable (playlist_Middle) to hold the name of the middle playlist. In order to get the names of the tracks in that playlist, we have to go back to iTunes, but instead of building a whole new tell block, I decided to introduce the concept of the one-line tell. If you just need a little bit of information from an application, a one-line tell works famously. You just tell the application what to do (in short, concise terms), and it does it. In this line, we told iTunes to go get us the names of every track of a specific playlist, using the same kind of syntax we used earlier to get the name of all the playlists. It has to be done this way, since tracks are contained by playlists. If you go to the iTunes dictionary, and look up tracks, you will see that this is the case. You can’t just tell iTunes to get you the name of every track; you have to specify a playlist, since they are the objects that contain all the tracks.

This script contains mind boggling information.

If you enjoy the concept of one-liners, check this baby out:

tell application "iTunes" to set all_Middle_tracks to the name of every track of playlist ((get name of every playlist)'s middle item)

It is the exact same script we have just built, but reduced to a complete one-liner. Don’t worry if you don’t like it, many of us prefer our scripts to be bigger and more obvious anyway.

All right, this has sure been fun, but generating lists with AppleScript can be even more powerful than this. From here on out, we are going to access the entire iTunes library, which is your first playlist. You can refer to it by name or number, neither iTunes nor AppleSCript cares which. Let’s say you like cats. (I like cats.) You want to examine your playlist for any track that has the word ‘cat’ in it. Try this:

tell application "iTunes"
	set cat_Tracks to the name of every track of playlist 1 whose name contains "cat"
end tell

Is that not cool? It is fast, efficient, and darn near self-explanatory. You just want the names of every track of your whole library (playlist 1) whose name contains “cat”, and this does it for you. My list looks like this:

{“Catch a Wave”, “Stray Cat Strut”, “Musicatto”}

As you can see, this kind of a search returns any track title with the letters “cat” right next to each other. (I have emboldened that in my list above; it will NOT show like that in your Results pane.) If you just want the word ‘cat’ all by itself, simply change your string to this:

whose name contains " cat "

By inserting spaces at either end of the search term, it will only return names that have that word isolated. So, now my list is a list of one item:

{“Stray Cat Strut”}

You can also add multiple parameters to build your list. Let’s take a quick look at the iTunes dictionary one more time. Go to the entry for track, and look at duration, rating, and podcast. You learn that once again, duration is the length of the track in seconds. You also learn that rating is an integer from 0 to 100. (Here is a perfect example of the weakness of certain dictionary entries. You know that in iTunes, you have ratings that are anywhere from 0 to 5 stars. The dictionary certainly could tell you that the rating integer of 20 is 1 star, 40 is two stars, etc. It doesn’t, so I am telling you.) Finally, the podcast property is what is called boolean. This means either true or false. Your track is either a podcast, or it isn’t. Boom. That simple.

For this list, we are going to assume that you have a fetish for the letter ‘T.’ You also only feel like short songs today, like less than 3 minutes. Of course, you don’t want any old boring podcasts, so you tell iTunes to make a list of tracks with title words that begin with ‘t’, are less than 3 minutes long, and are not podcasts. Here we go:

tell application "iTunes"
	set short_T_non_Podcast to the name of every track of playlist 1 whose name contains " t" and duration is less than (3 * 60) and podcast is false
end tell

First, we invent a variable name that we like, tell iTunes to search the whole library (playlist 1) looking for names that contain " t". (Note the space before the letter ‘t’.) This will pick up ANY word in the title that begins with ‘t’, not just the first word. We also want duration to be less than 3 minutes, and we know that the duration property is in seconds, so we do a little math and multiply 60 (seconds) by 3. We also want the podcast property to be false, so we put that in as well. The pattern to remember for mulitple properties is something like this:

set YOUR_VARIABLE to every WHATEVER YOU WANT of WHATEVER CONTAINS WHAT YOU WANT whose PROPERTY (is, contains, is not, etc.) and PROPERTY (is, contains, is not, etc.) and PROPERTY (is, contains, is not, etc.).

I do not know if there is a limit on property searches, but I know that I have used up to 6 in one line before, and it is still pretty darn fast. The Relational Operators available for use are extensive. The vintage 1999 Applescript Language Guide is still a good place to look at the details. The page you want is Operators, which is in the “The Language at a Glance” section. (That section is near the bottom of the contents listing, and Operators is about halfway down in that section → Found here, and you don’t have to be a member to read it.)

Once you get the hang of it, you can do all sorts of well… sorts. It is very, very powerful mojo. The only drawback is that these types of relational searches only work on Application objects, not on AppleScript objects. Since iTunes is an application, it works great. Unfortunately however, you cannot do this:

set my_Cool_List to {"One", "Two", "Three", "Four", "Five", "Six"}
get every item of my_cool_list that contains "t"

It will not even compile. You can actually get that information, but you need to loop through every item, and we will cover that another time.

Let’s briefly take a look at a list of lists. Run this baby:

(Depending on how many playlists you have in your iTunes library, you may need to change the 10 to a smaller number.)

tell application "iTunes"
	set playlist_Two to the name of every track of playlist 2
	set playlist_Ten to the name of every track of playlist 10
end tell
set both_Playlists to {playlist_Two, playlist_Ten}
count both_Playlists

Your result is 2. Even though playlist_Two and playlist_Ten each contain multiple titles, your final list of both_PLaylists only contains 2 items: the two lists.

Want to see something else interesting? Change that next to the last line to this:

set both_Playlists to {playlist_Two, (count playlist_Two), playlist_Ten, (count playlist_Ten)}

Your count is now 4. There are 4 items in the list. Item 1 is the list of track names for playlist 2, item 2 is the count of that list, item 3 is the list of track names of playlist 10, and item 4 is the count of that list. This line will give you those counts:

item 2 of both_Playlists & item 4 of both_Playlists

With AppleScript, you can make huge lists of whatever data you want, in any kind of order you want. You are limited only by your imagination and ability to extract or create the information you desire. You should also learn to comment your scripts as well, ESPECIALLY when creating large, potentially cumbersome lists. For example, I personally enjoy scripting my Palm Pilot software. Once in awhile, I need to extract certain information from either the date book or the address book, so I will whip up a script that generates a long list of lists. Here is how I comment that sort of thing:

--Each list here is {Last Name, First Name, Zip, email}

I put that at the end of the line that creates the list in the first place. That way, when I am ready to process the list of data later on in the script, I have a handy reference to how each list is constructed.

Since we are focusing on iTunes, let’s also take a quick look at a list of references to objects. This will actually be more important in the future than just track names. Run this, and then look at your Results pane:

tell application "iTunes"
	set all_Q_Tracks to every track of playlist 1 whose name contains "Q"--Note that we are setting the list to the TRACKS, not the track names
end tell

(If you get an empty list, just put a different letter in place of the Q.)
Your results are pretty confusing, aren’t they? That’s because when we set up this list, we actually put the tracks themselves into the list, not their names. This list contains references to selected tracks, so that when iTunes refers to them again, it goes to the track itself. This is important for things like using AppleScript to make playlists for you automatically, or even for deleting tracks from your library. It will also become more important as you learn to script other applications and need to make lists of those objects and do something with them, or to them.

Here is another script to digest until our next session:

Be sure to look in the iTunes dictionary to try to understand any new terms

tell application "iTunes"
	make new playlist with properties {name:"Fav Long A Songs"}
	duplicate (every track of playlist 1 whose name starts with "A" and duration is greater than (3 * 60) and rating is greater than or equal to 60) to playlist "Fav Long A Songs"
end tell