How to get names of record properties?

Hi bmose,

When I was working with Automator, I noticed that they passed the input values records in this form:

set the clipboard to {a:1, b:"2", c:3}
set the_clipboard to the clipboard
--> {list:{"a", 1, "b", "2", "c", 3}}

The value is the record as key value pairs.

This only happened within the past 7 years I think.

gl,

Nice discovery, kel1. It’s comforting to learn that it is a fairly new feature, given all the contortions that used to be required to extract record labels. Thanks for sharing your great solution.

kel1,

I did find one limitation of the “list of (the clipboard)” technique: barred labels lose their bars when their text values are retrieved from the clipboard. That shortcoming could be overcome by searching each returned label for characters that are invalid in Applescript names, and placing bars around such labels. It wouldn’t work for labels which are valid Applescript variable names but are barred because they are reserved keywords: |name|, |word|, etc.

Alternatively, one could use the text item delimiter approach I submitted yesterday.

Hi bmose,

Yeah i saw that. Thinking.

Later,
kel

Hi bmose,

I was thinking that what is the purpose of knowing if there where bars. The only reason i can think of is to convert the string label back to a variable label. So, if this is the purpose, then maybe try/error statements might find if you need the bars. Need to test this. Is it rarely that you use vertical bars?

Later,
kel

Hi bmose,

I can’t think of anything good about the bars. One thing might be to add tags when you use the bars like ||. Otherwise, your way.

gl,
kel

An ASObjC-based library in Mavericks is another alternative:

use framework "Foundation"

on labelsOfRec:theRec
	set theRec to current application's NSDictionary's dictionaryWithDictionary:theRec
	return (theRec's allKeys()) as list
end labelsOfRec:

Called with:

use theLib : script "<lib name>"

set theRec to {a:1, |b:"2"|:3}
theLib's labelsOfRec:theRec
--> {"a", "b:\"2\""}

kel, it is rare that I use barred labels. “Barring” that unusual circumstance, your method works perfectly well.

Incidentally, the question has been posed as to why one would want to retrieve record labels in the first place. One common scenario in my experience involves handlers, which I like to construct with a single record as the input argument. The record’s properties are given descriptive labels, which remind me of the purpose of each input parameter. At the beginning of the handler code, I retrieve the labels and confirm that valid labels are present in and invalid labels are absent from the input record.

Shane, with just the slightest inkling thus far of the capabilities of Maverick libraries, it seems that they have the potential to be a paradigm shifter in the Applescript universe.

Prolonging an already long thread, but I wanted to share some lessons learned and an improved method of getting a record’s labels.

Just a brief comment about limitations of three methods recently described:

(1) my method of converting a record to its text representation, then delimiting the text with the text representation of the record’s property values:

  • this fails when a property value that is a reference is resolved into its underlying value in the process of converting to text

(2) kel1’s method of saving a record to the clipboard, then parsing the clipboard’s “list” property:

  • returns barred labels without the bars

(3) Shane Stanley’s method using NSDictionary

  • requires access to ASObjC in one form or another; fails to return the label of record properties whose value = missing value (presumably because of the way Cocoa handles missing value in an NSDictionary); and like kel1’s method, returns barred labels without the bars

The following method utilizes kel1’s method along with a “run script” construct to determine if a label is barred or not. It returns the record’s labels and values as lists:

set theRecord to {a:1, |b b|:2, c:3, |âœŽâœ“ï ƒï „ï£¿|:4}

set the clipboard to theRecord
tell (the clipboard)'s list
	set {recordLabels, recordValues} to {{}, {}}
	repeat with i from 1 to (length - 1) by 2
		set {labelWithoutBars, labelWithBars, currValue} to {item i, "|" & item i & "|", item (i + 1)}
		try
			tell me to run script "{" & labelWithoutBars & ":null}"
			set {end of recordLabels, end of recordValues} to {labelWithoutBars, currValue}
		on error
			try
				tell me to run script "{" & labelWithBars & ":null}"
				set {end of recordLabels, end of recordValues} to {labelWithBars, currValue}
			on error m number n
				error "Could not get the record's labels: (" & n & ") " & m
			end try
		end try
	end repeat
end tell

{recordLabels, recordValues} --> {{"a", "|b b|", "c", "|âœŽâœ“ï ƒï „ï£¿|"}, {1, 2, 3, 4}}

One limitation of this method is that a record cannot be saved to the clipboard when one or more of its items’ values is an application reference, such as a Finder file reference or a System Events property list, etc. A way around this limitation is to wrap such a reference in a script object property and set the value of the record item to the script object, which then allows the record to be saved to the clipboard.

Just one point about the pipes (bars): they are not part of the label. Pipes are added when a script is compiled and are not stored as part of the label. A good example is where you might use |someThing| because someThing is a terminology clash with a scripting addition – when you open the script without the addition, the pipes disappear.

(The missing value thing is arguably a bug.)

Thank you for the clarification about the nature and role of pipes.