In Finder Deselect Folders and Files of Various Extensions

I’ve got this script that deselects all the folders from my selection in Finder. I’d like to extend it to also deselect files with specific extensions:

my deselectAllFolders()

on deselectAllFolders()
   script o
       property sel : {}
   end script
   tell application "Finder"
       set o's sel to selection as alias list
       set tc to count o's sel
       repeat with i from 1 to tc
           if class of item (item i of o's sel) is folder then set item i of o's sel to missing value
       end repeat
       set selection to aliases of o's sel -- select all items without folders 
   end tell
   return "" -- to not put all files into the result (it's very slow to show an huge list in the editor )
end deselectAllFolders

I’ve tried:

set selection to aliases of filterFilesByExtension(selection, "txt")

And a few similar combinations of things, but I can’t get it to work. Any suggestions?

UPDATE - Someone has suggested this:

my deselectFoldersAndItemsWithTheseExtensions({"rtf", "pdf"})

on deselectFoldersAndItemsWithTheseExtensions(exclusionsList)
   script o
       property sel : {}
   end script
   tell application "Finder"
       set o's sel to selection as alias list
       set tc to count o's sel
       repeat with i from 1 to tc
           set thisItem to item i of o's sel
           if ((class of item thisItem is folder) or (name extension of thisItem is in exclusionsList)) then set item i of o's sel to missing value
       end repeat
select aliases of o's sel -- select all items without folders 
   end tell
   return "" -- to not put all files into the result (it's very slow to show an huge list in the editor )
end deselectFoldersAndItemsWithTheseExtensions

Which has potential but for some reason on my machine is doing the wrong thing. Here’s my repro steps and the results:

  1. In finder set the view to “list”
  2. Expand a folder that contains some files with deselectFoldersAndItemsWithTheseExtensions extensions
  3. Multi select the folder and the files inside it
  4. Launch the application created with this script

Result: Finder pops up a new window with the view set to Icon and displaying the inside of the folder.
Expected Result. Same Finder window, all folders and files with deselectFoldersAndItemsWithTheseExtensions extensions are now deselected.

Running the script in Sierra, I only get a new window when the selected items are on the desktop — and that’s because I was forgetting the Finder’s select command behaves identically to its reveal command nowadays. Sorry about that. Reverting the ‘select’ line to your original ‘set selection’ should cure it.

my deselectFoldersAndItemsWithTheseExtensions({"rtf", "pdf"})

on deselectFoldersAndItemsWithTheseExtensions(exclusionsList)
	script o
		property sel : {}
	end script
	tell application "Finder"
		set o's sel to selection as alias list
		set tc to count o's sel
		repeat with i from 1 to tc
			set thisItem to item i of o's sel
			if ((class of item thisItem is folder) or (name extension of thisItem is in exclusionsList)) then set item i of o's sel to missing value
		end repeat
		set selection to aliases of o's sel -- select only the items which haven't been eliminated.
	end tell
	return "" -- to not put all files into the result (it's very slow to show an huge list in the editor )
end deselectFoldersAndItemsWithTheseExtensions

If you’re running the script as an application, I presume it’s from a dock or script menu. (It doesn’t need to be an application in the latter case.) Running by double-clicking it would change the selection, of course.

I actually run it by saving it as an application and putting it into the toolbar of Finder.

This new script works really well, but for some reason it’s not working for .nfo files. I definitely drops folders, mkv and txt files, but for some reason my nfo’s are staying selected.

I tried adding this to debug with, but it just printed a bunch of gibberish

set myFile to open for access (choose file name) with write permission
			write name of thisItem to myFile
			write name extension of thisItem to myFile
			close access myFile

That’s really strange! For some reason, the Finder’s returning the name extensions of files with names ending in “.nfo” as “”! :confused: I presume it’s a bug.

The only workaround I can think of immediately (using the Finder, at least) is to check each name to see if it ends with a dot and one of the excluded extensions:

my deselectFoldersAndItemsWithTheseExtensions({"rtf", "pdf", "nfo"})

on deselectFoldersAndItemsWithTheseExtensions(exclusionsList)
	script o
		property sel : {}
	end script
	
	-- Prepend a dot to each excluded extension if it doesn't already have one.
	repeat with thisExtension in exclusionsList
		if (thisExtension does not start with ".") then set thisExtension's contents to "." & thisExtension
	end repeat
	
	tell application "Finder"
		set o's sel to selection as alias list
		set tc to count o's sel
		repeat with i from 1 to tc
			set thisItem to item i of o's sel
			if (class of item thisItem is folder) then
				-- Replace this alias in the list with a 'missing value' if it represents a folder …
				set item i of o's sel to missing value
			else
				-- … or if its name ends with one of the dot-prepended extensions in exclusionsList.
				set thisName to name of thisItem
				repeat with thisExtension in exclusionsList
					if (thisName ends with thisExtension) then
						set item i of o's sel to missing value
						exit repeat
					end if
				end repeat
			end if
		end repeat
		set selection to aliases of o's sel -- select only the items which haven't been eliminated.
	end tell
	return "" -- to not put all files into the result (it's very slow to show an huge list in the editor )
end deselectFoldersAndItemsWithTheseExtensions

But it may be that System Events or ASObjC would do a better job. I’ll look into it and get back.

Yeah. They both recognise “nfo” extensions.

System Events:

my deselectFoldersAndItemsWithTheseExtensions({"rtf", "pdf", "nfo"})

on deselectFoldersAndItemsWithTheseExtensions(exclusionsList)
	script o
		property sel : {}
	end script
	
	tell application "Finder" to set o's sel to selection as alias list
	
	set tc to count o's sel
	repeat with i from 1 to tc
		set thisItem to item i of o's sel
		tell application "System Events"
			if ((class of disk item (thisItem as text) is folder) or (name extension of thisItem is in exclusionsList)) then set item i of o's sel to missing value
		end tell
	end repeat
	
	tell application "Finder" to set selection to aliases of o's sel -- select only the items which haven't been eliminated.
	return "" -- to not put all files into the result (it's very slow to show an huge list in the editor )
end deselectFoldersAndItemsWithTheseExtensions

ASObjC:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

my deselectFoldersAndItemsWithTheseExtensions({"rtf", "pdf", "nfo"})

on deselectFoldersAndItemsWithTheseExtensions(exclusionsList)
	script o
		property sel : {}
	end script
	
	tell application "Finder" to set o's sel to selection as alias list
	
	set |⌘| to current application
	set URLArray to |⌘|'s class "NSArray"'s arrayWithArray:(o's sel)
	set exclusionsArray to |⌘|'s class "NSArray"'s arrayWithArray:(exclusionsList)
	set directoryAndPackageKeys to |⌘|'s class "NSArray"'s arrayWithArray:({|⌘|'s NSURLIsDirectoryKey, |⌘|'s NSURLIsPackageKey})
	set folderResult to |⌘|'s class "NSDictionary"'s dictionaryWithDictionary:({NSURLIsDirectoryKey:true, NSURLIsPackageKey:false})
	
	repeat with i from 1 to (count o's sel)
		set thisURL to item i of URLArray
		if (((thisURL's resourceValuesForKeys:(directoryAndPackageKeys) |error|:(missing value))'s isEqualToDictionary:(folderResult)) or ¬
			(exclusionsArray's containsObject:(thisURL's pathExtension()))) then
			set item i of o's sel to missing value
		end if
	end repeat
	
	tell application "Finder" to set selection to aliases of o's sel -- select only the items which haven't been eliminated.
	return "" -- to not put all files into the result (it's very slow to show an huge list in the editor )
end deselectFoldersAndItemsWithTheseExtensions

Yay you did it. My hero :smiley:

Here’s a case-insensitive version of the ASObjC script.

use AppleScript version "2.5" -- El Capitan (10.11) or later
use framework "Foundation"
use scripting additions

my deselectFoldersAndItemsWithTheseExtensions({"RTF", "pdf", "nfo"})

on deselectFoldersAndItemsWithTheseExtensions(exclusionsList)
	tell application "Finder" to set sel to selection as alias list
	
	set |⌘| to current application
	set URLArray to |⌘|'s class "NSMutableArray"'s arrayWithArray:(sel)
	-- Get an array of lower-cased versions of the excluded extensions.
	set exclusionsArray to (|⌘|'s class "NSArray"'s arrayWithArray:(exclusionsList))'s valueForKey:("lowercaseString")
	set directoryAndPackageKeys to |⌘|'s class "NSArray"'s arrayWithArray:({|⌘|'s NSURLIsDirectoryKey, |⌘|'s NSURLIsPackageKey})
	set folderResult to |⌘|'s class "NSDictionary"'s dictionaryWithObjects:({true, false}) forKeys:(directoryAndPackageKeys)
	
	-- Remove any URLs from the URL array which represent folders or whose path extensions, lower-cased, match any of the lower-cased excluded extensions.
	repeat with i from (URLArray's |count|()) - 1 to 0 by -1
		set thisURL to (URLArray's objectAtIndex:(i))
		if (((thisURL's resourceValuesForKeys:(directoryAndPackageKeys) |error|:(missing value))'s isEqualToDictionary:(folderResult)) or ¬
			(exclusionsArray's containsObject:(thisURL's pathExtension()'s lowercaseString()))) then
			tell URLArray to removeObjectAtIndex:(i)
		end if
	end repeat
	
	-- Coerce the array of remaining URLs to an AS list of «class furl».
	set newSel to URLArray as list
	tell application "Finder" to set selection to newSel -- select only the items which haven't been eliminated.
	return "" -- to not put all files into the result (it's very slow to show an huge list in the editor )
end deselectFoldersAndItemsWithTheseExtensions

Edits: Replaced the originally posted script with a different approach.
Corrected the minimum AS version required following Shane’s comment below.

Nigel,

That script requires 10.11 for the file-NSURL coercion.

You could also simplify it a little with:

	set exclusionsArray to (current application's NSArray's arrayWithArray:exclusionsList)'s valueForKey:"lowercaseString"

And then the repeat loop like this:

	repeat with thisURL in URLArray's reverseObjectEnumerator()'s allObjects()
		if ((thisURL's resourceValuesForKeys:(directoryAndPackageKeys) |error|:(missing value))'s isEqualToDictionary:(folderResult)) or (exclusionsArray's containsObject:(thisURL's pathExtension()'s lowercaseString())) then
			 URLArray's removeObject:thisURL
		end if
	end repeat

Hi Shane.

Thanks for the minimum system version correction, which I’ve now edited in the script above. I’d already changed the script to the lower-casing regime before you posted, but used a decrementing AS index in the repeat instead of a reversed array and removal by value. I’d guess that the index approach is more efficient, but is it?

It probably is in isolation, but I suspect the actual time of either is barely noise compared with what ASObjC itself adds. Certainly, last I did some tests, using repeat with was generally quite a bit quicker.