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.
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
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.
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.
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
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
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