Filtering NSArray of NSURLs for filename contains and other properties

I’m trying to filter an array of NSURLs for items containing specific values in some property. For example, if the filename of an NSURL contains a specific string. I can easily loop through the list and perform the checks, I’d prefer to do this with predicates if possible, but I don’t know them well enough yet to do so.

This handler filters by file extensions. I’d like to create similar handlers for multiple other NSURL properties.

set NSURLArray to FileSystem_List_Files(path to the desktop folder from user domain)
set fileExtensionList to {"txt", "zip"}
NSURLs_Matching_File_Extension(NSURLArray, fileExtensionList)

on NSURLs_Matching_File_Extension(NSURLArray, fileExtensionList)
	set fileExtensionList to (current application's NSArray's arrayWithArray:fileExtensionList)'s valueForKey:"lowercaseString"
	set thePredicate to current application's NSPredicate's predicateWithFormat_("pathExtension.lowercaseString IN %@", fileExtensionList)
	return (NSURLArray's filteredArrayUsingPredicate:thePredicate)
end NSURLs_Matching_File_Extension

Could someone show me the equivalent code for filename contains, filename matches, and/or filename does not contain?

What other NSURL properties can be filtered using predicates? i.e. Is there a document listing all other keys that can be used like “pathExtension”?

Just guessing but isn’t this it?

That is indeed the documentation for it. Unfortunately getting from there to functional code is difficult. There is so much information included and much of it isn’t applicable to AppleScript. If I could extract the proper information I’m sure that I could answer my own question. It’s a bit like needing a drink and getting the fire hose.

1 Like

FWIW, I feel similarly about the NSStuff.

Paul. The following scripts return those URLs whose file names contain and do not contain a search string:

use framework "Foundation"
use scripting additions

--this code section creates an array of URLs for testing purposes only
set thePaths to {"/Users/Robert/Desktop/Test File.txt", "/Users/Robert/Desktop/Test Match File.txt", "/Users/Robert/Desktop/Test File.zip", "/Users/Robert/Desktop/Test Match File.zip", "/Users/Robert/Desktop/Test File.pdf"}
set theURLs to current application's NSMutableArray's new()
repeat with aPath in thePaths
	set aURL to (current application's |NSURL|'s fileURLWithPath:aPath)
	(theURLs's addObject:aURL)
end repeat

--return an array of URLs that contain a search string in the file name
set matchingURLs to getMatchingFilesOne(theURLs, "Match") --the parameters are the URLs and search string
on getMatchingFilesOne(theURLs, matchString)
	set thePredicate to current application's NSPredicate's predicateWithFormat_("lastPathComponent CONTAINS %@", matchString)
	return theURLs's filteredArrayUsingPredicate:thePredicate
end getMatchingFilesOne

--return an array of URLs that do not contain a search string in the file name
set matchingURLs to getMatchingFilesTwo(theURLs, "Match") --the parameters are the URLs and search string
on getMatchingFilesTwo(theURLs, matchString)
	set thePredicate to current application's NSPredicate's predicateWithFormat_("NOT (lastPathComponent CONTAINS %@)", matchString)
	return theURLs's filteredArrayUsingPredicate:thePredicate
end getMatchingFilesTwo

The above scripts use the CONTAINS operator to make string comparisons, but the following can be substituted:

  • == checks for equality.
  • BEGINSWITH and ENDSWITH do what you would expect.
  • LIKE allows the use of * and ? wildcard characters.
  • MATCHES allows the use of a regular expression.

You can make these operators case and/or diacritic insensitive by appending [c], [d], or [cd] to the operator (e.g. CONTAINS[c]).

I don’t know of a list of the URL properties that can be filtered on. I typically use lastPathComponent, path extension, and path. You can also do something like the following, which filters on file names but without their extension.

--incorporate theURLs from earlier
set matchingURLs to getMatchingFiles(theURLs, "test match file") --the parameters are the URLs and file names without extension
on getMatchingFiles(theURLs, matchString)
	set thePredicate to current application's NSPredicate's predicateWithFormat_("URLByDeletingPathExtension.lastPathComponent ==[c] %@", matchString)
	return theURLs's filteredArrayUsingPredicate:thePredicate
end getMatchingFiles

It’s often useful to apply two or more predicates, and the following is an example:

--return an array of URLs that have a specified file extension and that do not contain a search string in the file name
--incorporate theURLs from earlier
set matchingURLs to getMatchingFilesOne(theURLs, "txt", "Match") --the parameters are the URLs, file extension, and search string
on getMatchingFilesOne(theURLs, theExtension, matchString)
	set thePredicate to current application's NSPredicate's predicateWithFormat_("pathExtension ==[c] %@ AND NOT lastPathComponent CONTAINS[c] %@", theExtension, matchString)
	return theURLs's filteredArrayUsingPredicate:thePredicate
end getMatchingFilesOne
3 Likes

Thank you Peavine! This is exactly the right info and level of detail I was hoping for. Your examples are excellent and give me enough repetition and variation to build on. I particularly appreciate the compound predicate bit.

1 Like