DekGenius.com
[ Team LiB ] Previous Section Next Section

13.13 Record

A record is an unordered collection of name-value pairs. The values may be of any type. A literal record looks like a literal list except that each item has a name. The name is separated from the corresponding value with a colon. So:

set R to {who:"Matt", town:"Ojai"}

There is no such thing as an empty record. A record has no item elements, its items cannot be referred to by index number, and you can't talk about the beginning or end of a record.

You can assign a record of values to a literal record of variable names or other references as a shorthand for performing multiple assignment. The assignments are performed pairwise by name, independently. If the record of values includes names that aren't in the record of variables, the extra values are ignored; if it's missing any names that are in the record of variables, there's a runtime error. See Section 7.1 and Section 13.12, earlier in this chapter. For example:

local who, town
set {who:who, town:town} to {town:"Ojai", who:"Matt"}
{who, town} -- {"Matt", "Ojai"}

When you use set (as opposed to copy) to set a variable to a value which is a record, you set the variable by reference. This means that the record is not copied; the variable's name becomes a new name for the record, in addition to any names for the record that may already exist. The same is true when a record is passed as a parameter to a handler. This special treatment is in common between lists, records, dates, and script objects. (See Section 8.4 and Section 9.5.1.)

For example:

set R2 to {who:"Matt", town:"Ojai"}
set R1 to R2
set who of R2 to "Jaime"
R1 -- {who:"Jaime", town:"Ojai"}

In an existing record, you can replace the value of individual items by assigning to an item of a record using a name that already exists in that record. But if you wish to create an item with a name that doesn't already exist in that record, you must make a new record using concatenation. So:

set R to {who:"Matt", town:"Ojai"}
set who of R to "Jaime"
R -- {who:"Jaime", town:"Ojai"}
set R to R & {friend:"Steve"}
R -- {who:"Jaime", town:"Ojai", friend:"Steve"}

There is no penalty for concatenating an item with a name that already exists in a record, but it has no effect. For example:

set R to {who:"Matt", town:"Ojai"} & {who:"Jaime"}
R -- {who:"Matt", town:"Ojai"}

Clearly, order is all-important here, and you can use this fact to your advantage. Suppose you want to assign a friend value within a record. If the record already has such an item, you can do it by assignment. If it doesn't, you can do it by concatenation. But what if you don't know whether it has such an item or not? You can do it regardless by concatenating in the opposite order:

set R to {who:"Jaime", town:"Ojai"}
set R to {friend:"Steve"} & R
R -- {friend:"Steve", who:"Jaime", town:"Ojai"}

set R to {who:"Jaime", town:"Ojai", friend:"Matt"}
set R to {friend:"Steve"} & R
R -- {friend:"Steve", who:"Jaime", town:"Ojai"}

A record can recurse, for the same reason that a list can:

set R to {who:"Matt", town:"Ojai", cycle:null}
set cycle of R to R
R -- {who:"Matt", town:"Ojai", cycle:{who:"Matt", town:"Ojai", cycle:...}}

You can make records mutually recurse; you can even make a list and record that mutually recurse. Does anyone have an aspirin?

13.13.1 Record Properties

The following are the properties of a record:


length

The number of items in the record. This property is read-only. You can get the same information by sending the record the count message.


The names of the items

Every name of every item is a property of the record.

Please pretend now that I'm jumping up and down, waving a big red flag and screaming as I repeat, for emphasis: the names of a record's items are properties. The names are not strings; the names are not any kind of variable or value. They are effectively tokens created by AppleScript at compile-time, like the names of variables.

When you talk to a record, it is the target, and its item names are used to interpret the vocabulary you use. The first thing AppleScript does is look to see whether any of this vocabulary is the name of an item of the record. That's why you can't assign to a nonexistent item in a record—the name you're using for that item is meaningless. No terminological confusion normally arises, because the context is clear. So:

set town to "Ojai"
set R to {name:"Matt", town:null}
set town of R to town -- no problem

Of course, you can confuse AppleScript if you set your mind to it. This code just sets the existing value of the variable town to itself; the record is untouched:

set town to "Ojai"
set R to {name:"Matt", town:null}
tell R
        set town to town
end tell
R -- {name:"Matt", town:null}

But you know how to fix that—right?

set town to "Ojai"
set R to {name:"Matt", town:null}
tell R
        set its town to town
end tell
R -- {name:"Matt", town:"Ojai"}

There is no good way to obtain a list of the names of the items of a record. A record has no such introspective abilities. You (a human being) can see the names of the items of a record in AppleScript's display of the record's value. But your code can't see this information; the names of the items are not values of any kind, and cannot easily be turned into values. I have seen many elaborate attempts to work around this problem, but I'm not going to show you any of them. This is a big shortcoming of AppleScript itself, and it needs to be fixed on the level of AppleScript itself.

It is possible to fetch or assign to the value of an item of a record using a variable to represent the name, through a second level of evaluation. Here's a way to fetch an item's value:

global r
set r to {nam:"Matt", age:"49"}
on getWhat(what)
        set s to "on run {r}" & return
        set s to s & "get " & what & " of r" & return
        set s to s & "end"
        run script s with parameters {r}
end getWhat
getWhat("age")

It's a pity that such trickery is needed, and I don't really recommend this approach. See also Section 14.6 in the next chapter.

    [ Team LiB ] Previous Section Next Section