Automatic reordering of possible elements added to the end of the basic name of the selected files

Hello.

Using small scripts, I add optional personal information at the end of some file names (specifically, if they have already been viewed “ :face_with_monocle:”, if I consider them to be of bad quality “b. q”, or if I liked them “ :+1:” and, if they exist, they appear in that order).

To get the icons to rearrange themselves in the default order when added, I wrote the script shown below (it trims the existing personal information at the end of the name and adds the sorted result of the new information, if necessary).

To simplify things, I have replaced the name of a file with text.

It works well, but I think the algorithm is very clumsy because it breaks down all possible cases one by one.

Furthermore, my approach is only suitable for adding a specific icon, so it would be necessary to write a similar script for each of the other two elements that can be added.

The goal is to find a more concise algorithm that does not break down the possible cases one by one and, if possible, is valid for any of the optional elements.

use AppleScript version "2.4"
use scripting additions

property one : " 🧐" -- Already seen
property two : " b. q" -- bad quality
property three : " 👍" -- OK

global nBasic

--If all optional elements are present, the preferred order is " 🧐 b. q 👍"}

--set nBasic to "This is the usual test 👍 🧐 b. q" --   TEST  →  → This is the usual test 🧐 b. q 👍
--set nBasic to "This is the usual test b. q" --  TEST  → "This is the usual test 🧐 b. q"
--set nBasic to "This is the usual test 🧐"   --  TEST  → "This is the usual test 🧐"
-- etc.

set nBasic to "This is the usual test 👍 b. q" 

set nBasic to addOne(nBasic)
display dialog "nBasic → " & nBasic --  TEST   → This is the usual test 🧐 b. q 👍


on addOne(_nBasic)
	if one is not in _nBasic then
		if two is in _nBasic and three is in _nBasic then -- " b. q 👍" / " 👍 b. q"
			return (text 1 thru -(((length of two) + (length of three)) + 1) of _nBasic) & one & two & three
		end if
		
		if two is in _nBasic then -- " b. q"  --  
			return (text 1 thru -(((length of two)) + 1) of _nBasic) & one & two
		end if
		
		if three is in _nBasic then --  " 👍"
			return (text 1 thru -(((length of three)) + 1) of _nBasic) & one & three
		end if
		
		if two is not in _nBasic and three is not in _nBasic then --   if there are none
			set _nBasic to _nBasic & one
			return _nBasic & one
		end if
		
	else -- one is in _nBasic  --  " 🧐"
		if two is in _nBasic and three is in _nBasic then -- " 🧐" & (" b. q 👍" / " 👍 b. q")  (in any order) → " 🧐 b. q 👍" 
			return (text 1 thru -(((length of one) + (length of two) + (length of three)) + 1) of _nBasic) & one & two & three
		end if
		
		if two is in _nBasic then -- " 🧐" +  " b. q" (in any order) → " 🧐 b. q"
			return (text 1 thru -(((length of one) + (length of two)) + 1) of _nBasic) & one & two
		end if
		
		if three is in _nBasic then --  " 🧐" +  " 👍"  (in any order)
			return (text 1 thru -(((length of one) + (length of three)) + 1) of _nBasic) & one & three
		end if
		
		if two is not in _nBasic and three is not in _nBasic then
			return _nBasic
		end if
	end if
	
end addOne

At least replace the if - endif blocks with a if - else if - endif chain. The last else respectively doesn’t need a condition because it will be executed if the other conditions evaluate to false

on addOne(_nBasic)
	if one is not in _nBasic then
		if two is in _nBasic and three is in _nBasic then -- " b. q 👍" / " 👍 b. q"
			return (text 1 thru -(((length of two) + (length of three)) + 1) of _nBasic) & one & two & three
			
		else if two is in _nBasic then -- " b. q"  --  
			return (text 1 thru -(((length of two)) + 1) of _nBasic) & one & two
			
		else if three is in _nBasic then --  " 👍"
			return (text 1 thru -(((length of three)) + 1) of _nBasic) & one & three
			
		else
			return _nBasic & one
		end if
		
	else -- one is in _nBasic  --  " 🧐"
		if two is in _nBasic and three is in _nBasic then -- " 🧐" & (" b. q 👍" / " 👍 b. q")  (in any order) → " 🧐 b. q 👍" 
			return (text 1 thru -(((length of one) + (length of two) + (length of three)) + 1) of _nBasic) & one & two & three
			
		else if two is in _nBasic then -- " 🧐" +  " b. q" (in any order) → " 🧐 b. q"
			return (text 1 thru -(((length of one) + (length of two)) + 1) of _nBasic) & one & two
			
		else if three is in _nBasic then --  " 🧐" +  " 👍"  (in any order)
			return (text 1 thru -(((length of one) + (length of three)) + 1) of _nBasic) & one & three
			
		else
			return _nBasic
		end if
	end if
	
end addOne

Consider to use tags or labels for this kind of annotations. You can add custom labels. One benefit is you can create smart folders to show the items dynamically.

First of all, thank you very much, StefanK, for your willingness to help me.

The code for the endless “if” blocks has been simplified somewhat with the use of “else if” after nesting the first condition. This will be useful for me in future scripts.

on addOne(_nBasic)
	if one is not in _nBasic then
		if two is in _nBasic and three is in _nBasic then -- " b. q 👍" / " 👍 b. q"
			return (text 1 thru -(((length of two) + (length of three)) + 1) of _nBasic) & one & two & three
			
		else if… (etc.)

I considered using your suggestion to use tags, but I rejected it because it is less visible than adding small elements at the end of the file name, and also because I was hoping to find an algorithm that would allow me to combine elements to achieve a certain order (I didn’t know how to do it, and the only thing I could think of was to cut part of the file name and then literally add the order of the manually sorted elements).

I would appreciate it if you could explain a little more about using smart folders to display items dynamically.

Suggestions are still pending for unifying addOne(_nBasic), addTwo(_nBasic), and addThree(_nBasic) into a single handler.

Thank you again.

I was talking about two different things: Tags, which appear in the Finder Info window and labels (File labels in terms of Spotlight)
Screenshot 2025-08-06 at 18.09.23

which appear in list view. Both can be used in search patterns to create smart folders. For example when you select New Smart Folder you can specify

Screenshot 2025-08-06 at 18.14.06

Personally I’m using file labels all the time to organize files and folders.

Thank you very much, StefanK, for showing me this new feature.

I avoid using tags; I prefer to use information that is in the file name.

As far as I can tell, if I understand correctly, it is equivalent to saving a search with the added feature that new items that meet the criteria specified in the filter will also be included in the search. In my case, I have not used tags, but rather " :+1:" OR " b. q" OR " :face_with_monocle:" (from Finder, not from Spotlight).

It’s difficult to implement an universal algorithm, because there is no efficient/fast way to identify the information token(s). It would help for example to specify a unique separator between file name and tokens rather than a space character which can occur a dozen times in the file name.

If you know the range of the tokens in the file name you could create a handler to serialize them. For example 🧐 b. q becomes 01, then you can insert the index at the proper position, deserialize (convert) the indices back to the emojis/text equivalents and replace the token range with the new value.

This requires some coding effort but is likely more efficient than implementing three add... handlers.

e.g. “This is the usual test” (If “:face_with_monocle:” OR “b. q” OR “:+1:” does not exist, add it to the end of the file name, adding the separator “-” when adding the first token) → “This is the usual test” ⇒ “This is the usual test - token1st”.
Once a token has been added, the separator will already exist and it will not be necessary to add it again.

I’m not sure if I understand this part correctly…
e.g. If “:face_with_monocle:” is 01 and “b. q” is 02 and “:+1:” is 03 → how would you continue?

We add the first token (e.g. 02:
“This is the usual test” ⇒ “This is the usual test - 02” ⇒ “This is the usual test - b. q”

We will add a second token at a later time (e.g. 01):
“This is the usual test - b. q” ⇒ “This is the usual test - 02 01”

We reorder the fragment behind " -": 02 01 ⇒ 01 02
“This is the usual test - 02 01” ⇒ “This is the usual test - 01 02”

We replace it with its value:
“This is the usual test - 01 02” ⇒ “This is the usual test - :face_with_monocle: b. q”

I’m not sure how to convert e.g. “02 01” to “01 02” or e.g. “03 01 02” to “01 02 03.”

No, 01 (as string) means it contains the first and second token (in almost all programming languages the first index of a sequence is zero). But it can be 12 instead of 01 because AppleScript’s first index is one.

The main problem with regard to efficiency is the amount of space characters. For a universal algorithm you need to separate This is the usual test and 👍 b. q to avoid the text 1 thru -(((length of two) + (length of three) spaghetti code. I recommend also to indicate the lack of a token with x to keep the position of the character, for example 🧐 👍 becomes 1x3

This is a staring point. It requires an underscore character between the file name and the tokens.
It won’t work without it.
The script displays the source string, the token to add and the result

property one : "🧐" -- Already seen
property two : "b. q" -- bad quality
property three : "👍" -- OK

set nBasic to "This is the usual test_ 👍 b. q"
display dialog nBasic
set {baseText, serializedToken} to serializeBasic(nBasic)
set newIndex to "1"
display dialog "Will Add 🧐"
set newToken to addToken(serializedToken, newIndex)
set nBasicNew to baseText & "_" & deserializeBasic(newToken)
display dialog nBasicNew

on serializeBasic(theText)
	set {saveTID, text item delimiters} to {text item delimiters, {"_"}}
	set {base, tokens} to text items of theText
	set AppleScript's text item delimiters to saveTID
	set theIndex to ""
	if tokens contains one then
		set theIndex to theIndex & "1"
	else
		set theIndex to theIndex & "x"
	end if
	if tokens contains two then
		set theIndex to theIndex & "2"
	else
		set theIndex to theIndex & "x"
	end if
	if tokens contains three then
		set theIndex to theIndex & "3"
	else
		set theIndex to theIndex & "x"
	end if
	return {base, theIndex}
end serializeBasic

on deserializeBasic(theText)
	set returnValue to ""
	if theText contains "1" then set returnValue to returnValue & space & one
	if theText contains "2" then set returnValue to returnValue & space & two
	if theText contains "3" then set returnValue to returnValue & space & three
	return returnValue
end deserializeBasic

on addToken(serializedToken, i)
	if serializedToken contains i then return serializedToken
	if i is "1" then
		return i & text 2 thru 3 of serializedToken
	else if i is "2" then
		return text 1 of serializedToken & i & text 3 of serializedToken
	else
		return text 1 thru 2 of serializedToken & i
	end if
end addToken

1 Like

After reading your latest script, I realized that I misunderstood everything you were trying to explain to me.
Thanks to this latest script, (understanding it gave me a headache) but it was very satisfying to finally get it.

I can’t express how grateful I am for your time and effort.
I congratulate you for guiding me through your handlers until I understood it and all the learning that comes with it.
The results are amazing, achieving all the objectives.

All you need to do is change the value of

set newIndex to “1” to any other value

to make it valid for any token and make small changes so that the number of possible tokens can be expanded to as many as you want.
It’s wonderful.
Thank you very much, StefanK