Changing the values stored in a record

I am trying to use records for the first time and have run into an issue when trying to store data in a record.
Here is the function code in full:

set TID to AppleScript's text item delimiters
	set AppleScript's text item delimiters to "."
	
	set tFileList to {} -- parent list of records
	set tTxtPosn to 0
	set tPath to ""
	set tRec to {FilePathKey:"filepathkey", Basepath:"basename", Extn:"extn", RawSrc:""}
	# pString is delimited by return so paragraphs means lines 
	repeat with i from 1 to (count of paragraphs of pString)
		set tLine to paragraph i of pString as string
		set tTxtPosn to offset of "-->" in tLine -- does line contain -->
		if tTxtPosn > 0 then
			# Line contains old and new file paths	
			set tTxtPosn to tTxtPosn + 5 -- adjust to begining of new file path
			set tPath to text tTxtPosn thru -2 of tLine as string -- this is the key value
			log "Tpath is : " & tPath
			set tBasePath to text item 1 of tPath
			set tExtn to text item 2 of tPath
			
			set FilePathKey of tRec to tPath
			set Basepath of tRec to tBasePath
			set Extn of tRec to tExtn
			--set tRec to {FilePathKey:tPath, Basepath:tBasePath, Extn:tExtn, RawSrc:""}
			
			set end of tFileList to tRec
		end if
	end repeat

Focusing on the lines that are causing the issue
First I set a dummy record tRec

set tRec to {FilePathKey:"filepathkey", Basepath:"basename", Extn:"extn", RawSrc:""}

Next I write new values to the record inside a repeat loop before adding it to the parent list:

set FilePathKey of tRec to tPath     <-- only updates tRec on first pass thru loop
set Basepath of tRec to tBasePath    <-- only updates tRec on first pass thru loop
set Extn of tRec to tExtn            <-- only updates tRec on first pass thru loop
--set tRec to {FilePathKey:tPath, Basepath:tBasePath, Extn:tExtn, RawSrc:""}  <-- updates on every pass through loop

The issue is that the top three set commands of the group only update the record on the first pass through the loop. However when the line “set tRec to {FilePathKey:tPath, Basepath:tBasePath, Extn:tExtn, RawSrc:”"} is uncommented the record is updated on every pass.

So why is the code failing to update the record on subsequent passes through the loop when it addresses a property yet updates on the first pass? I suspect I am seeing an issue with references but I have no idea what is happeninng.

Advice / instruction welcomed

best wishes
Simon

Can you give a small example of

pString

?

yes, here is my test text string:

set tResult to “Volumes/Images_Disc_02_Master/Test_SDCard/OM540001.JPG
Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540001.JPG
‘Volumes/Images_Disc_02_Master/Test_SDCard/OM540001.JPG’ → ‘/Volumes/Images_Disc_02_Master/IngestTesting2/2025/2025_02/2025_02_14_135505_OM540001.JPG’
Created directory /Volumes/Images_Disc_02_Master/IngestTesting2/2025/2025_02
======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540001.xmp
Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540001.xmp
Nothing changed in Volumes/Images_Disc_02_Master/Test_SDCard/OM540001.xmp
======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540003.xmp
Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540003.xmp
Nothing changed in Volumes/Images_Disc_02_Master/Test_SDCard/OM540003.xmp
======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540002.JPG
Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540002.JPG
‘Volumes/Images_Disc_02_Master/Test_SDCard/OM540002.JPG’ → ‘/Volumes/Images_Disc_02_Master/IngestTesting2/2025/2025_02/2025_02_14_135517_OM540002.JPG’
======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540003.JPG
Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540003.JPG
‘Volumes/Images_Disc_02_Master/Test_SDCard/OM540003.JPG’ → ‘/Volumes/Images_Disc_02_Master/IngestTesting2/2025/2025_02/2025_02_14_135524_OM540003.JPG’
======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540004.ORF
Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540004.ORF
‘Volumes/Images_Disc_02_Master/Test_SDCard/OM540004.ORF’ → ‘/Volumes/Images_Disc_02_Master/IngestTesting2/2025/2025_02/2025_02_14_135527_OM540004.ORF’
======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540002.xmp
Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540002.xmp
Nothing changed in Volumes/Images_Disc_02_Master/Test_SDCard/OM540002.xmp
======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540001.ORF
Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540001.ORF
‘Volumes/Images_Disc_02_Master/Test_SDCard/OM540001.ORF’ → ‘/Volumes/Images_Disc_02_Master/IngestTesting2/2025/2025_02/2025_02_14_135505_OM540001.ORF’
======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540004.JPG
Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540004.JPG
‘Volumes/Images_Disc_02_Master/Test_SDCard/OM540004.JPG’ → ‘/Volumes/Images_Disc_02_Master/IngestTesting2/2025/2025_02/2025_02_14_135527_OM540004.JPG’
======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540003.ORF
Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540003.ORF
‘Volumes/Images_Disc_02_Master/Test_SDCard/OM540003.ORF’ → ‘/Volumes/Images_Disc_02_Master/IngestTesting2/2025/2025_02/2025_02_14_135524_OM540003.ORF’
======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540002.ORF
Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540002.ORF
‘Volumes/Images_Disc_02_Master/Test_SDCard/OM540002.ORF’ → ‘/Volumes/Images_Disc_02_Master/IngestTesting2/2025/2025_02/2025_02_14_135517_OM540002.ORF’
======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540004.xmp
Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540004.xmp
Nothing changed in Volumes/Images_Disc_02_Master/Test_SDCard/OM540004.xmp
1 directories scanned
1 directories created
8 image files updated
4 image files unchanged”

I see that the forum has mangled the text so here is a second attempt :slight_smile:

Simon

set tResult to "Volumes/Images_Disc_02_Master/Test_SDCard/OM540001.JPG
		Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540001.JPG
		'Volumes/Images_Disc_02_Master/Test_SDCard/OM540001.JPG' --> '/Volumes/Images_Disc_02_Master/IngestTesting2/2025/2025_02/2025_02_14_135505_OM540001.JPG'
		Created directory /Volumes/Images_Disc_02_Master/IngestTesting2/2025/2025_02
		======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540001.xmp
		Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540001.xmp
		Nothing changed in Volumes/Images_Disc_02_Master/Test_SDCard/OM540001.xmp
		======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540003.xmp
		Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540003.xmp
		Nothing changed in Volumes/Images_Disc_02_Master/Test_SDCard/OM540003.xmp
		======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540002.JPG
		Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540002.JPG
		'Volumes/Images_Disc_02_Master/Test_SDCard/OM540002.JPG' --> '/Volumes/Images_Disc_02_Master/IngestTesting2/2025/2025_02/2025_02_14_135517_OM540002.JPG'
		======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540003.JPG
		Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540003.JPG
		'Volumes/Images_Disc_02_Master/Test_SDCard/OM540003.JPG' --> '/Volumes/Images_Disc_02_Master/IngestTesting2/2025/2025_02/2025_02_14_135524_OM540003.JPG'
		======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540004.ORF
		Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540004.ORF
		'Volumes/Images_Disc_02_Master/Test_SDCard/OM540004.ORF' --> '/Volumes/Images_Disc_02_Master/IngestTesting2/2025/2025_02/2025_02_14_135527_OM540004.ORF'
		======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540002.xmp
		Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540002.xmp
		Nothing changed in Volumes/Images_Disc_02_Master/Test_SDCard/OM540002.xmp
		======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540001.ORF
		Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540001.ORF
		'Volumes/Images_Disc_02_Master/Test_SDCard/OM540001.ORF' --> '/Volumes/Images_Disc_02_Master/IngestTesting2/2025/2025_02/2025_02_14_135505_OM540001.ORF'
		======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540004.JPG
		Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540004.JPG
		'Volumes/Images_Disc_02_Master/Test_SDCard/OM540004.JPG' --> '/Volumes/Images_Disc_02_Master/IngestTesting2/2025/2025_02/2025_02_14_135527_OM540004.JPG'
		======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540003.ORF
		Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540003.ORF
		'Volumes/Images_Disc_02_Master/Test_SDCard/OM540003.ORF' --> '/Volumes/Images_Disc_02_Master/IngestTesting2/2025/2025_02/2025_02_14_135524_OM540003.ORF'
		======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540002.ORF
		Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540002.ORF
		'Volumes/Images_Disc_02_Master/Test_SDCard/OM540002.ORF' --> '/Volumes/Images_Disc_02_Master/IngestTesting2/2025/2025_02/2025_02_14_135517_OM540002.ORF'
		======== Volumes/Images_Disc_02_Master/Test_SDCard/OM540004.xmp
		Setting new values from Volumes/Images_Disc_02_Master/Test_SDCard/OM540004.xmp
		Nothing changed in Volumes/Images_Disc_02_Master/Test_SDCard/OM540004.xmp
		    1 directories scanned
		    1 directories created
		    8 image files updated
		    4 image files unchanged"

Although I haven’t found the specific documentation for how it is implemented (yet), a record is one of the few AppleScript objects that actually acts like an object - that is, although you can change the values of the keys, the record identifier will always be a reference to that record. The result is that updating the record keys actually winds up updating all the items in your file list, as they are referring to the same object. The following is a simplified script that demonstrates this:

set template to {Foo:"first", Bar:"second", Baz:"third"}
set output to {}
repeat with anItem in words of "ONE TWO THREE"
   set anItem to anItem as text
   set Foo of template to anItem
   set Bar of template to anItem
   set Baz of template to anItem
   log template
   set end of output to template
   log output
end repeat
return output

So when you specifically declare a new record, that is what gets added to the list. Another way to add the updated item to the list would be to make a copy of the (current) record, for example copy tRec to end of tFileList.

@red_menace … Is this it?

Under: Declaring Variables with the copy Command

Or you can search for ‘gamma’ in the ASLG.

You could use a combination of contains, lists, and text item delimiters to extract your data and it should be more efficient.

To make the script easier to read in Script Editor, I deposited your text string in a text file on the desktop and read it in. You could also let the read command split up the text.

use scripting additions

set srcText to (path to desktop as text) & "zres"
set paraStr to read file srcText as «class utf8»

set par to paragraphs of paraStr
set lp to length of par --> 41

set tFileList to {}
set text item delimiters to {"--> '", "'", "."}

repeat with eachPara in par
	if eachPara contains " --> " then
		
		set tPath to text item 5 of eachPara
		set tExtn to text item 6 of eachPara
		
		-- to avoid duplicate paths
		-- if tFileList does not contain tPath then
		set end of tFileList to {tPath, tExtn}
		-- end if
	end if
end repeat
tFileList


(*
-- these are the resulting text items of each line containing '-->'
text items of item 3 of par
--> {"		", "Volumes/Images_Disc_02_Master/Test_SDCard/OM540001", "JPG", " ", "/Volumes/Images_Disc_02_Master/IngestTesting2/2025/2025_02/2025_02_14_135505_OM540001", "JPG", ""}
*)

I noticed that the container paths for the jpegs and the orfs are identical. It is commented out but I added an if…then test to prevent duplicate entries in tFileList.

At the bottom, I added a line to provide an example of exactly what will be extracted from the result lines in your string when using the specified delimiters.

Correct. A record is one of the 4 AppleScript mutable objects, so if you are not aware of the difference between set and copy with those, it can sometimes result in a surprise.

1 Like

#Red_Menace # Mockman

Thanks for the link to the documentation which I think I understand. Am I correct in believing the following about properties and how my code snip from above is operating?

set FilePathKey of tRec to tPath     <-- only updates tRec on first pass thru loop
set Basepath of tRec to tBasePath    <-- only updates tRec on first pass thru loop
set Extn of tRec to tExtn            <-- only updates tRec on first pass thru loop

The variable tRec has been set the a record type before the loop starts. The record has three properties FilePathKey, BasedPath and Extn. These three lines just change the values stored in these properties of this one and only record, i.e. a new record is not being created. Whereas the following line sets the variable tRec to point to a whole new record which just happens to have the same properties as the first version of tRec.

--set tRec to {FilePathKey:tPath, Basepath:tBasePath, Extn:tExtn, RawSrc:""}  <-- updates on every pass through loop

Simon

I’ve been experimenting with your code and running into some issues which are all to do with setting variables to list items and getting references by accident. Is using your construct

text item n

the same as

the contents of item n as text

S

Answering my own question - no they are not. text item returns the nth block of text as delimited by the text delimiters set above.

All correct. One thing to add… when you use as text on text items, you will join those items around the first delimiter. So ‘text items of’ will split text on the delimiters, and ‘as text’ will join on the first (or only) delimiter. The processes are, more or less, symmetrical. A common usage is search and replace.

And as a side note, you can do both in the same command (as long as the first delimiter is not nothing, ie "" or empty quotes).

I followed advice given above and refactored by code to use lists and lists of lists rather than records. When using lists it is rather simple to get a reference rather than a value returned and the automatic dereferencing that display dialog uses only serves to confuse (me).

The short code that Mockman published above has, like Topsy, grown a little. The function accepts a string as input and reduces it to a list of file paths. Next is uses this initial list to produce three other lists each of which gets used in slightly different ways when making calls to exiftool.

on ImageFiles(pString)
	# Creates three lists based on contents of result string (pString) from exif tool
	
	
	# List 1. List of raw and dng files that have jpeg sidecar files .
	# JPEGs are updated by passing raw and dng file paths and using srcfile command
	
	# List 2. List of raw files used to create and write data into new xmp sidecar files.
	
	# List 3. List of all dng files plus any non sidecar or solo jpeg files 
	# These files will be written to directly.
	
	# List of valid raw file extensions, extend/reduce as needed.	
	set RawExtns to {"ORF", "RW2", "nef", "CRW", "CR2", "CR3"}
	
	# ++++++++++++++++++++++++ Parse input build 3 lists of files based on file extension ++++++++++++++
	
	set paraStr to pString
	
	set par to paragraphs of paraStr
	set lp to length of par --> 41
	local tRawFiles, tDngFiles, tJpgFiles, tBaseName, tBasePath, tExtn, DetailsLst
	set tRawFiles to {}
	set tDngFiles to {}
	set tJpgFiles to {}
	set text item delimiters to {" --> '", "'", "."}
	
	repeat with eachPara in par
		if eachPara contains " --> " then
			
			set tBasePath to text item 5 of eachPara --basename
			set tExtn to text item 6 of eachPara --extension
			
			set tFullPath to tBasePath & "." & tExtn
			
			# store file details in one of three lists depending on file extension
			if RawExtns contains tExtn then
				set end of tRawFiles to {tFullPath, tBasePath}
			else if tExtn = "dng" then
				set end of tDngFiles to {tFullPath, tBasePath}
			else if tExtn = "jpg" then
				set end of tJpgFiles to tBasePath
			end if
			
		end if
	end repeat
	
	# ++++++++++++++++++++++++++++++++++++++ BUILD LISTS +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++	
	#Now compare the raw file list with the list of jpegs
	
	set RawDngPairedwithJPGFiles to {} --*
	set SoloJpegFiles to {} --*
	set PairedJpegs to {} --*
	set tRawDngBasePaths to {} --*
	set CombinedList to {} --*
	
	
	# Build list of raw and dng files that have associated  jpeg sidecar files
	set CombinedList to tRawFiles & tDngFiles
	repeat with DetailsLst in CombinedList
		set tBaseName to contents of item 2 of DetailsLst -- read the base path of raw file
		
		if tJpgFiles contains tBaseName then
			set end of RawDngPairedwithJPGFiles to contents of item 1 of DetailsLst
			set end of PairedJpegs to ((contents of item 2 of DetailsLst) & ".jpg") -- do we care ?
		end if
		
	end repeat
	
	# simplify Combined list
	repeat with DetailsLst in CombinedList
		# build flat list of dng raw base file paths
		set end of tRawDngBasePaths to contents of item 2 of DetailsLst
	end repeat
	
	
	# tJPGfiles is a flat list of just base paths of jpeg files
	repeat with DetailsLst in tJpgFiles
		set tBaseName to contents of DetailsLst -- read the base path of raw file
		
		if tRawDngBasePaths contains tBaseName then
			# ignore, no need to add to record as jpeg will be updated using raw/dng filepath and srcfile
		else
			# This jpeg is not a sidecar, its on its own in folder so append extension and add to list 
			set end of SoloJpegFiles to ((contents of DetailsLst) & ".jpg")
		end if
		
	end repeat
	
	
	# Prepare the output lists
	
	set All_DNGfiles to {}
	set All_RAWfiles to {}
	set All_EditableFiles to {}
	
	#! Simplify list means parse a list of lists and reduce it to a new list of file paths,
	
	# Simplify the list of dng files removing the base path
	repeat with DetailsLst in tDngFiles
		set end of All_DNGfiles to contents of item 1 of DetailsLst
	end repeat
	
	# Simplify the list of raw files removing the base path
	repeat with DetailsLst in tRawFiles
		set end of All_RAWfiles to contents of item 1 of DetailsLst
	end repeat
	
	# Populate new list of all directly editable image files i.e. solo jpegs and dng.
	set All_EditableFiles to SoloJpegFiles & All_DNGfiles
	
	# Need to return all three lists so place them inside a new list
	set ListWrapper to {}
	set ListWrapper to {RawDngPairedwithJPGFiles, All_RAWfiles, All_EditableFiles}
	
	return ListWrapper
	
end ImageFiles

Thanks for all your help.

Simon

1 Like