This is an ASObjC-based library for a SmartRecord object. AppleScript records have several limitations: you can’t get the keys/labels as strings, you can’t delete items from a record, and you can’t coerce them to text. Unfortunately the scripting bridge ASObjC relies on has a limitation too: it drops entries whose value is missing value. The SmartRecord will return an error if you pass it a record with a value set to missing value (or if one of the labels is an AppleScript keyword).
use framework "Foundation"
script BaseObject -- the parent script object, required for AppleScriptObjC inheritance
end script
on smartRecordWith:aRecord -- call to make a new smartRecord
script SmartRecord
property parent : BaseObject -- for inheritance
property dictStore : missing value -- where the dictionary is stored
on objectForKey:theKey -- returns object for the key, or missing value if not found
set theResult to dictStore's objectForKey:theKey
-- if its an array, coerce to a list
if ((theResult's isKindOfClass:(current application's NSArray)) as integer = 1) then
return theResult as list
else -- coerce to list and return item 1; workaround to coerce item of unknown class
set theResult to theResult as list
return item 1 of theResult
end if
end objectForKey:
on allKeys() -- returns list of keys
return dictStore's allKeys() as list
end allKeys
on allValues() -- returns list of values
return dictStore's allValues() as list
end allValues
on allKeysForObject:theValue -- return all keys with the given value
return (dictStore's allKeysForObject:theValue) as list
end allKeysForObject:
on setObject:theValue forKey:theKey -- modify or add value for key
dictStore's setObject:theValue forKey:theKey
end setObject:forKey:
on setObjects:theValues forKeys:theKeys -- modify or add values for keys
set newDict to current application's NSDictionary's dictionaryWithObjects:theValues forKeys:theKeys
dictStore's addEntriesFromDictionary:newDict
end setObjects:forKeys:
on objectsForKeys:theKeys notFoundMarker:theMarker -- return values for keys, and use marker where not found
return (dictStore's objectsForKeys:theKeys notFoundMarker:theMarker) as list
end objectsForKeys:notFoundMarker:
on keysSortedByValue() -- all keys, in order based on their values
return (dictStore's keysSortedByValueUsingSelector:"compare:") as list
end keysSortedByValue
on removeObjectForKey:theKey -- delete entry
dictStore's removeObjectForKey:theKey
end removeObjectForKey:
on removeObjectsForKeys:theKey -- delete entries
dictStore's removeObjectsForKeys:theKey
end removeObjectsForKeys:
on addEntriesFromDictionary:newDict -- add dictionary
dictStore's addEntriesFromDictionary:newDict
end addEntriesFromDictionary:
on asDictionary() -- return as a dictionary for further use
return dictStore
end asDictionary
on asRecord() -- return as a normal record
return dictStore as record
end asRecord
on asText() -- return as text
set theKeys to dictStore's allKeys() as list
set theString to current application's NSMutableString's |string|()
repeat with i from 1 to count of theKeys
(theString's appendString:(current application's NSString's stringWithFormat_(", %@:%@", item i of theKeys, dictStore's objectForKey:(item i of theKeys))))
end repeat
return "{" & ((theString's substringFromIndex:2) as text) & "}"
end asText
end script
-- set up the initial script object
if class of aRecord is record then
set theDict to current application's NSMutableDictionary's dictionaryWithDictionary:aRecord
-- bug means conversion drops items where value is set to missing value, so we check the number before and after
if theDict's |count|() as integer is not length of (aRecord as list) then error "Record cannot be converted (contains missing value?)" number -10000
set dictStore of SmartRecord to theDict
else -- empty record aka list
set dictStore of SmartRecord to current application's NSMutableDictionary's dictionary()
end if
return SmartRecord
end smartRecordWith:
In use:
use theLib : script "<name of lib>"
set theDict to theLib's smartRecordWith:{firstName:"Frieda", lastName:"Rhode"}
log theDict's asText()
theDict's setObjects:{"Katrine", 32} forKeys:{"firstName", "age"}
log theDict's asText()
log theDict's objectsForKeys:{"firstName", "address"} notFoundMarker:"not found"
log (theDict's allKeysForObject:32)
log theDict's allKeys()
log theDict's allValues()
theDict's removeObjectForKey:"lastName"
log theDict's asText()