Hi.
It’s not so much AppleScript that’s slow, but the Finder itself and the communications between it and the script. The trick is to have the script issue as few commands to the Finder as possible and to make those commands as effective as possible.
The script below issues one command to the Finder to return the names and sizes of all the files in the folder, which the Finder can do quite quickly. (A shell script is probably even quicker.) The script itself, using vanilla AppleScript, sorts the size and name lists in parallel and searches for runs of equal sizes. Only with runs of two or more is the Finder then scripted at the level of individual files to set their label indices.
-- CustomQsort by Arthur Knapp and Nigel Garvey.
on CustomQsort(theList, l, r, customiser)
script o
property cutoff : 10
property comparer : me
property slave : me
property lst : theList
on qsrt(l, r)
set pivot to my lst's item ((l + r) div 2)
set i to l
set j to r
repeat until (i > j)
set u to my lst's item i
repeat while (comparer's isGreater(pivot, u))
set i to i + 1
set u to my lst's item i
end repeat
set w to my lst's item j
repeat while (comparer's isGreater(w, pivot))
set j to j - 1
set w to my lst's item j
end repeat
if (i > j) then
else
set my lst's item i to w
set my lst's item j to u
slave's swap(i, j)
set i to i + 1
set j to j - 1
end if
end repeat
if (j - l < cutoff) then
else
qsrt(l, j)
end if
if (r - i < cutoff) then
else
qsrt(i, r)
end if
end qsrt
on isrt(l, r)
set u to my lst's item l
repeat with j from (l + 1) to r
set v to my lst's item j
if (comparer's isGreater(u, v)) then
set here to l
set my lst's item j to u
repeat with i from (j - 2) to l by -1
tell my lst's item i
if (comparer's isGreater(it, v)) then
set my lst's item (i + 1) to it
else
set here to i + 1
exit repeat
end if
end tell
end repeat
set my lst's item here to v
slave's shift(here, j)
else
set u to v
end if
end repeat
end isrt
on isGreater(a, b)
(a > b)
end isGreater
on swap(a, b)
end swap
on shift(a, b)
end shift
end script
set listLen to (count theList)
if (listLen > 1) then
if (l < 0) then set l to listLen + l + 1
if (r < 0) then set r to listLen + r + 1
if (l > r) then set {l, r} to {r, l}
if (customiser's class is script) then
try
customiser's isLess
set o's comparer to customiser
end try
try
customiser's swap
customiser's shift
set o's slave to customiser
end try
else if (customiser's class is record) then
set {comparer:o's comparer, slave:o's slave} to customiser & {comparer:o, slave:o}
end if
if (r - l + 1 > o's cutoff) then o's qsrt(l, r)
if (r > l) then o's isrt(l, r)
end if
return
end CustomQsort
on main()
-- Script object with "slave" code for CustomQsort. Sort another list in parallel with the one being sorted.
script parallel
property lst : missing value
on swap(a, b)
tell item a of my lst
set item a of my lst to item b of my lst
set item b of my lst to it
end tell
end swap
on shift(a, b)
tell item b of my lst
repeat with i from b - 1 to a by -1
set item (i + 1) of my lst to item i of my lst
end repeat
set item a of my lst to it
end tell
end shift
end script
-- Another script object for speed of access to these lists' items.
script o
property nameList : missing value
property sizeList : missing value
end script
tell application "Finder"
activate
tell front Finder window
set current view to list view
set sort column of its list view options to size column
set theFolder to its target as text
end tell
-- Get all the names and sizes with one command.
set {o's nameList, o's sizeList} to {name, size} of files of folder theFolder
end tell
-- Sort the size list and the name list in parallel with it.
set parallel's lst to o's nameList
CustomQsort(o's sizeList, 1, -1, {slave:parallel})
-- Search for runs of equal values in the size list.
set i to 1
set currentSize to beginning of o's sizeList
repeat with j from 2 to (count o's sizeList)
set thisSize to item j of o's sizeList
if (thisSize is not currentSize) then
-- The current run has ended and a new one started. If there's more than one value in the completed run, get the equivalent names from the name list and set the label index of the files with those names.
if (j - i > 1) then setLabels(o, i, j, theFolder)
-- Reset the run-start markers for the new run.
set i to j
set currentSize to thisSize
end if
end repeat
-- Deal with any run in progress at the end of the repeat.
if (j - i > 1) then setLabels(o, i, j, theFolder)
end main
on setLabels(o, i, j, theFolder)
repeat with k from i to j - 1
set thisFileName to item k of o's nameList
tell application "Finder" to set label index of file (theFolder & thisFileName) to 1
end repeat
end setLabels
main()