I try to collect all IMAP Folders in Apple Mail and I want the complete folder paths. With this code I get all the references to the mailboxes:
tell application "Mail"
set folders to mailboxes
repeat with theAccount in accounts
set folders to folders & mailboxes of theAccount
end repeat
return folders
end tell
The entries don’t have a property with the path, but Script Debugger shows me this result. How to get the highlighted part of the objects with AppleScript?
Thanks, I already tried it, but building a list of 286 folders is quite slow with 4 seconds. Maybe this can be optimized? I thought it would be faster to access the information which seems to be already there.
tell application "Mail"
set folders to mailboxes
repeat with theAccount in accounts
set folders to folders & (mailboxes of theAccount)
end repeat
set allPaths to {}
repeat with theFolder in folders
set thePath to ""
set subFolder to theFolder
repeat
try
set thePath to name of subFolder & "/" & thePath
set subFolder to container of subFolder
on error
exit repeat
end try
end repeat
try
get name of account of theFolder
on error
set thePath to "Local/" & thePath
end try
set allPaths to allPaths & {characters 1 thru -2 of thePath as string}
end repeat
end tell
Direct list access take a long time. And indirect list access is faster than direct.
I don’t know about the mechanism. AppleScript’s array (list) can store various object.
So, its address calculation took a long time, I think.
We experienced this phenomenon in Classic MacOS era (a reference to).
Storing list in script object is new technic in Mac OS X era.
Normal list access script took 15 secs with my M1 Mac mini
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
property aList : {}
set a1Dat to current application's NSDate's timeIntervalSinceReferenceDate()
set aList to {}
repeat with i from 1 to 30000
set the end of aList to i
end repeat
repeat with i in aList
set aStr to i as string
end repeat
set b1Dat to current application's NSDate's timeIntervalSinceReferenceDate()
set c1Dat to (b1Dat - a1Dat)
--> 15.198549985886
Indirect list access (a reference to) took 0.44 secs
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
property aList : {}
set a1Dat to current application's NSDate's timeIntervalSinceReferenceDate()
set aList_ref to a reference to aList
set aList to {}
repeat with i from 1 to 30000
set the end of aList_ref to i
end repeat
repeat with i in aList_ref
set aStr to i as string
end repeat
set b1Dat to current application's NSDate's timeIntervalSinceReferenceDate()
set c1Dat to (b1Dat - a1Dat)
--> 0.439829945564
property in script object is the fastest access. It took 0.39 secs
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
script hgs
property aList : {}
end script
set a1Dat to current application's NSDate's timeIntervalSinceReferenceDate()
set (aList of hgs) to {}
repeat with i from 1 to 30000
set the end of (aList of hgs) to i
end repeat
repeat with i in (aList of hgs)
set aStr to i as string
end repeat
set b1Dat to current application's NSDate's timeIntervalSinceReferenceDate()
set c1Dat to (b1Dat - a1Dat)
--> 0.38650393486
That’s great info, thanks. The speed difference is indeed huge in your example.
Could reproduce the same results on my side, too.
I never knew about it. It also made me wonder why I never experienced any kind of noticeable delay when iterating through lists using the direct access.
I guess I just never had to deal with lists that contain thousands (let alone tens of thousands) of objects.
I ran some additional tests and it looks like that humanly noticeable difference in speed starts from about 4000 objects in your example.
Developer of the MsgFiler application for macOS here. There’s a much faster way to access the full path of mailboxes without having to go through the mailbox container hierarchy. It involves deliberately throwing an error when accessing a mailbox. The error message contains the full path to the mailbox.
Check out the following code which iterates through mailboxes in all accounts and the local On My Mac account.
tell application "Mail"
set theList to {}
set {TID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, quote}
-- All Accounts
repeat with a in accounts
repeat with m in mailboxes of a
try
m as rich text
on error e
set mailboxName to text item 2 of e
set theList to theList & mailboxName
end try
end repeat
end repeat
-- On My Mac
repeat with m in mailboxes
try
m as rich text
on error e
set mailboxName to text item 2 of e
set theList to theList & mailboxName
end try
end repeat
set AppleScript's text item delimiters to TID
return theList
end tell
Late thanks for this suggestion. It much faster and less complicated.
May I ask you, how you move the selection within mail in MsgFiler? I tried it with AppleScript without success. Well, I messed around with set selected messages of message viewer 1 to ….
It would be even faster to throw just one error for each list of mailboxes:
set theList to {}
tell application "Mail"
set accountsMailboxes to accounts's mailboxes
set macMailboxes to its mailboxes
end tell
set TID to AppleScript's text item delimiters
set AppleScript's text item delimiters to quote
-- All accounts.
repeat with a in accountsMailboxes
try
a as text
on error e
set tis to e's text items
repeat with i from 2 to (count tis) by 6
set my theList's end to my tis's item i
end repeat
end try
end repeat
-- On My Mac.
try
macMailboxes as text
on error e
set tis to e's text items
repeat with i from 2 to (count tis) by 4
set my theList's end to my tis's item i
end repeat
end try
set AppleScript's text item delimiters to TID
return theList
And here for interest is an alternative hack which seems to work. It’s intermediate in speed between Adam’s suggestion and my development of it and doesn’t use as much code:
tell application "Mail"
set accountsMailboxes to accounts's mailboxes
set macMailboxes to its mailboxes
end tell
set theList to {}
repeat with a in my accountsMailboxes
repeat with m in a
set my theList's end to (m as record)'s «class seld»
end repeat
end repeat
repeat with m in my macMailboxes
set my theList's end to (m as record)'s «class seld»
end repeat
return theList
I don’t know. But it’s not necessary. The account ids can be got by normal means. Something like this:
tell application "Mail"
set {accountIDs, accountsMailboxes} to accounts's {id, mailboxes}
set macMailboxes to its mailboxes
end tell
set theList to {}
repeat with i from 1 to (count accountIDs)
set suffix to " of account id " & accountIDs's item i
repeat with m in my accountsMailboxes's item i
set my theList's end to (m as record)'s «class seld» & suffix
end repeat
end repeat
repeat with m in my macMailboxes
set my theList's end to (m as record)'s «class seld»
end repeat
return theList