Speeding Up a repeat loop — Working with Mail.app and emails

Hey, everyone!

Can anyone suggest how I could speed up / improve this script?

I needed to solve a problem for a client.

They use the colored flags on Mail.app extensively for their business workflow. Different colors mean different things.

However, Mail.app colored flags aren’t visible in any environment other than a Mac. If anyone wants to use a PC or look at their emails in Webmail, the workflow is totally broken.

So I wrote a script that takes all flagged items and sorts them into corresponding folders on the server (Red, Green, Blue, etc). It then looks at those same folders, and removes anything that’s no longer flagged).

With this, the Mail.app colored flags are always synced up with a corresponding folder on the server.

However, they have a ton of emails, often with large attachments.

When the script runs, it can often slow down Mail.app, which I would like to avoid.

Any suggestions?


-- These are the account and folder names we need
set _account to "Jimthehandyman"
set _mailboxlistUNSET to {"INBOX/Red", "INBOX/Orange", "INBOX/Yellow", "INBOX/Green", "INBOX/Blue", "INBOX/Purple", "INBOX/Gray"}
set _mailboxlist to {"INBOX", "Sent Items"}


-- These are the color and index pairs so we can just iterate thru all of these with the same function
set _FlagRecord to {{index:"0", color:"Red"}, {index:"1", color:"Orange"}, {index:"2", color:"Yellow"}, {index:"3", color:"Green"}, {index:"4", color:"Blue"}, {index:"5", color:"Purple"}, {index:"6", color:"Gray"}}


-- Search thru each flag-folder, find any message without a flag, and move it into the inbox.
tell application "Mail"
	repeat with _m in _mailboxlistUNSET
		set allMessage to (every message of mailbox _m of account _account whose flag index is -1)
		repeat with MyMessage in allMessage
			move MyMessage to mailbox "INBOX" of account _account
		end repeat
	end repeat
end tell


-- Search through each color/flag pair and move flagged items into the flag-folder
tell application "Mail"
	repeat with i in _FlagRecord
		repeat with box in _mailboxlist
			
			-- here for testing
			-- display dialog (index of i) & (color of i)
			-- set _mailbox to box
			-- display dialog box
			
			set allMessage to (every message of mailbox _mailbox of account _account whose flag index is index of i)
			try
				repeat with MyMessage in allMessage
					move MyMessage to (first mailbox whose name is color of i) of account _account
				end repeat
			end try
		end repeat
	end repeat
end tell

I could only find one place that might be able to be sped up.
It is in the last inner-most repeat loop where you are moving emails to the “Color” mailbox.
Each time thru the repeat loop, you have the Mail program search for the box based on the name.
I added a variable “mbox” to the colored mailbox once before the loop starts.


-- These are the account and folder names we need
set _account to "Jimthehandyman"
set _mailboxlistUNSET to {"INBOX/Red", "INBOX/Orange", "INBOX/Yellow", "INBOX/Green", "INBOX/Blue", "INBOX/Purple", "INBOX/Gray"}
set _mailboxlist to {"INBOX", "Sent Items"}


-- These are the color and index pairs so we can just iterate thru all of these with the same function
set _FlagRecord to {{index:"0", color:"Red"}, {index:"1", color:"Orange"}, {index:"2", color:"Yellow"}, {index:"3", color:"Green"}, {index:"4", color:"Blue"}, {index:"5", color:"Purple"}, {index:"6", color:"Gray"}}


-- Search thru each flag-folder, find any message without a flag, and move it into the inbox.
tell application "Mail"
	repeat with _m in _mailboxlistUNSET
		set allMessage to (every message of mailbox _m of account _account whose flag index is -1)
		repeat with MyMessage in allMessage
			move MyMessage to mailbox "INBOX" of account _account
		end repeat
	end repeat
end tell


-- Search through each color/flag pair and move flagged items into the flag-folder
tell application "Mail"
	repeat with i in _FlagRecord
		repeat with box in _mailboxlist
			
			-- here for testing
			-- display dialog (index of i) & (color of i)
			-- set _mailbox to box
			-- display dialog box
			
			set allMessage to (every message of mailbox _mailbox of account _account whose flag index is index of i)
			try
				-- I added this below
				set mbox to (first mailbox whose name is color of i) of account _account
				repeat with MyMessage in allMessage
					move MyMessage to mbox -- now it doesn't have to search for each email
				end repeat
			end try
		end repeat
	end repeat
end tell

Since your request is commercial in nature, I’ll abstain from writing code samples, however, you should know that whose clauses tend to be less efficient with large file volumes than standard loops with nested if considerations, and you may want to experiment with a ground-up rewrite that removes them. As the messages’ flag index parallels the messages’ list position, there is little need to obtain the actual messages in a giant list, which is computationally expensive; they can simply be positionally (re)moved by index.

I didn’t tested because I don’t want to change my Mail’s datas but I assume that this edited version may do the job correctly.


-- These are the account and folder names we need
set _account to "Jimthehandyman"
set _mailboxlistUNSET to {"INBOX/Red", "INBOX/Orange", "INBOX/Yellow", "INBOX/Green", "INBOX/Blue", "INBOX/Purple", "INBOX/Gray"}
set _mailboxlist to {"INBOX", "Sent Items"}


set _colorNames to {"Red", "Orange", "Yellow", "Green", "Blue", "Purple", "Gray"}

-- Search thru each flag-folder, find any message without a flag, and move it into the inbox.
tell application "Mail" to tell account _account
	repeat with _m in _mailboxlistUNSET
		repeat with MyMessage in (every message of mailbox _m whose flag index is -1)
			move MyMessage to mailbox "INBOX"
		end repeat
	end repeat

-- Search through each color/flag pair and move flagged items into the flag-folder

	repeat with box in _mailboxlist
		repeat with MyMessage in (every message of mailbox box)
			set colorName to _colorNames's item (1 + (flag index of MyMessage))
			move MyMessage to (first mailbox whose name is _colorName)
		end repeat
	end repeat
end tell

As _mailboxlistUNSET, _mailboxlist and _colorNames are very short lists I assume that there is no need to try to fasten using the brave old property tip.
But as we aren’t changing these lists, it costs nothing to try to use such objects.


-- These are the account and folder names we need

property _mailboxlistUNSET : {"INBOX/Red", "INBOX/Orange", "INBOX/Yellow", "INBOX/Green", "INBOX/Blue", "INBOX/Purple", "INBOX/Gray"}
property _mailboxlist : {"INBOX", "Sent Items"}
property _colorNames : {"Red", "Orange", "Yellow", "Green", "Blue", "Purple", "Gray"}

set _account to "Jimthehandyman"

-- Search thru each flag-folder, find any message without a flag, and move it into the inbox.
tell application "Mail" to tell account _account
	repeat with _m in my _mailboxlistUNSET
		repeat with MyMessage in (every message of mailbox _m whose flag index is -1)
			move MyMessage to mailbox "INBOX"
		end repeat
	end repeat
	
	-- Search through each color/flag pair and move flagged items into the flag-folder
	
	repeat with box in my _mailboxlist
		repeat with MyMessage in (every message of mailbox box)
			set colorName to my _colorNames's item (1 + (flag index of MyMessage))
			move MyMessage to (first mailbox whose name is _colorName)
		end repeat
	end repeat
end tell

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) dimanche 28 juin 2020 18:31:02