A small note about AppleScripts – may be new to some (was to me )
I'm writing more JavaScript than AppleScript these days, but one of the basic structural constraints
of the AppleScript record type was always that you couldn't just ask a record for a list of its keys.
( In jargon terms, AppleScript records lack 'introspection' )
(Various kludges were possible – I used to use a slow and silly clipboard trick)
I noticed today, however, for the first time, (others probably saw it long ago)
that this occasionally exasperating limit has effectively been breached, since Yosemite,
by AppleScript's now improved access to ObjC library methods.
If we start a script with
use framework "Foundation"
We can now write things like:
use framework "Foundation"
on keysOfRecord(rec)
set ca to current application
return (ca's NSDictionary's dictionaryWithDictionary:rec)'s allKeys() as list
end keysOfRecord
keysOfRecord({|name|:"nemo", address:"Tokyo", |language|:"Latin", vehicle:"tandem"})
Can you help us with the next step: Getting the value from the record when the key is in a variable:
set myRec to {|name|:"nemo", address:"Tokyo", language:"Latin", vehicle:"tandem"}
set myKey to "address"
address of myRec ## This works
myKey of myRec ## FAILS with Can’t get myKey of . . .
Of course, we’d like to do something like this:
--- GET VALUES FOR ALL KEYS IN RECORD ---
set keyList to keysOfRecord(myRec)
repeat with oKey in keyList
set keyStr to text of oKey
set valueStr to keyStr of myRec ## FAILS
log (keyStr & ": " & valueStr)
end repeat
The next step is probably to switch to JavaScript, in which querying a dictionary is rather simpler:
JavaScript:
function run() {
var rec = {
name: "nemo",
address: "Tokyo",
language: "latin",
vehicle: "tandem",
profession: "nudnik"
}
return {
// List of keys in the record
keys: Object.keys(rec),
// List of values in the record
values: Object.keys(rec)
.map(function (key) {
return rec[key];
}),
// Each key paired with its value
pairs: Object.keys(rec)
.map(function (key) {
return [key, rec[key]];
})
}
}
AppleScript (skipping the details of testing the class of the ObjC value, and handling accordingly)
We could write code like that below, but it’s still clumsy. Chris may have more elegant suggestions for cracking this one.
use framework "Foundation"
on run {}
set rec to {|name|:"nemo", address:"Tokyo", |language|:1, vehicle:"tandem", profession:"nudnik"}
set lstKeys to keysOfRecord(rec)
return {keys:lstKeys, values:map(mClosure(justValue, {rec:rec}), lstKeys), pairs:map(mClosure(keyValPair, {rec:rec}), lstKeys)} ¬
end run
on keysOfRecord(rec)
set ca to current application
return (ca's NSDictionary's dictionaryWithDictionary:rec)'s allKeys() as list
end keysOfRecord
on maybeRecordValue(rec, strKey)
set ca to current application
set v to ((ca's NSDictionary's dictionaryWithDictionary:rec)'s objectForKey:strKey)
-- simplest case
return v as string
end maybeRecordValue
on justValue(strKey)
maybeRecordValue(my closure's rec, strKey)
end justValue
on keyValPair(strKey)
{strKey, maybeRecordValue(my closure's rec, strKey)}
end keyValPair
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
set mf to mReturn(f)
set lng to length of xs
set lst to {}
repeat with i from 1 to lng
set end of lst to mf's lambda(item i of xs, i, xs)
end repeat
return lst
end map
-- Handler -> Record -> Script
on mClosure(f, recBindings)
script
property closure : recBindings
property lambda : f
end script
end mClosure
-- Lift 2nd class function into 1st class wrapper
-- handler function --> first class script object
on mReturn(f)
if class of f is script then return f
script
property lambda : f
end script
end mReturn