Comparing contents of two lists

Mac OS 10.11.6

Et Al:
Folder “A” has 1,110 free-domain mp3 downloaded files
Folder “B” has 98 of those files that have been exported to iTunes and massaged.

I started writing a script (which I am sure can be downsized) to compare the files in both folders in order to get the names of the remaining 1,012 files so they can be massaged and put them into Folder “B” and iTunes.

FYI:
“Alias” and “A” variables are for the downloaded files (tracks)

“Final” and “B” variables are for the finished files (tracks)

So far:


-- Variables
set var_FolderAlias to ""
set var_AliasFiles to ""
set var_FolderFinal to ""
set var_FinalFiles to ""
set A to 0
set B to 0
set x to 1
set y to 0
--
-- Code
tell application "Finder"
	activate
-- 	
set target of Finder window 1 to folder "Superman (OTR)" of folder "Old Time Radio Library (Dropbox)" of folder "Dropbox" of folder "Dave" of folder "Users" of startup disk
	set var_FolderFinal to target of Finder window 1
	set var_FinalFiles to name of every file of entire contents of var_FolderFinal -- 98 files
	set B to count of var_FinalFiles
	--
	set target of Finder window 2 to folder "Superman (Ext HD)" of folder "Works-In-Progress" of disk "External HD 01"
	set var_FolderAlias to target of Finder window 2
	set var_AliasFiles to name of every file of entire contents of var_FolderAlias -- 1110 downloaded files
	set A to count var_AliasFiles
end tell

Q1: Removing matching items from var_FolderAlias will not remove the actual file on the HD, is that right? This would leave me with a list of the files I’m looking for.

Q2: Is there a search, lookup, or find command for applescript? This might be a faster way to look for matching files.

Sorry for any confusion,
BG

I wanted to start by getting some clarification on the goal here. It sounds to me like you’re saying you want a text list of the names of all the files in Folder A that don’t have a counterpart in Folder B. But if your goal is to then manually do something with all those files, then a text list sounds like sort of a PITA for output. Would it be better to move the duplicate files out of folder A, to another folder, or to tag all the duplicates with a Finder flag so you can sort the window based on which are/aren’t duplicates?

var_FolderAlias is an alias to the parent folder, so you can’t remove those items from an alias… they aren’t there, the alias is just a reference to the parent folder. So it depends on what you mean by “remove them.” If you remove them from the folder:
tell application finder to delete [an alias variable]
then the actual files are gone.

If you mean make an Applescript variable containing a list of aliases (or file names or whatever) and then remove items from that Applescript variable, then that has no effect on the actual files. ie, removing items from var_AliasFiles (which doesn’t contain aliases or any other kind of file reference, but a text list of the names of files) will have no effect on the actual files.

Performing search/find with Applescript is done with “do shell script” terminal commands, either sent to UNIX “find” or to Spotlight. But if you’re only dealing with a list of 1,110 files and 2 folders, I’d just stick with Applescript.

Let me know the real endgame here and this should be an easy thing to do. I just didn’t want to write it to generate a list of file names, leaving you to manually go through 1,110 files and look at each file to see which are on a text list containing 1,012 of them. That doesn’t sound like the best functionality from my limited understanding of your needs. It seems like it would be easier to just reference the folder of 98 duplicates as a list then to work off a text list of the other 1,012 non-duplicates.

Hi. I wrote the below vanilla AppleScript quite a few years ago to equalize two folders. It’s rudimentary and simply places the differences at the root level, but I think it may be adaptable to your purpose. If you want only one folder to equalize, disable one of the duplications or redirect the output elsewhere.

set FirstFolder to (choose folder with prompt "Choose the FIRST folder of this comparison.")
set SecondFolder to (choose folder with prompt "Choose the SECOND folder of this comparison.")


tell application "Finder"
	duplicate (((FirstFolder)'s entire contents's files whose name is not in (get (SecondFolder)'s entire contents's files's name)) as alias list) to SecondFolder
	duplicate (((SecondFolder)'s entire contents's files whose name is not in (get (FirstFolder)'s entire contents's files's name)) as alias list) to FirstFolder
end tell

Edit: The two “as alias list” bits can probably be removed without changing the functionality; they appear to be artifacts from a previous iteration.

As you are using 10.11.6 you may use ASObjC.

The code is long because I inserted in it handlers which in my real life are in libraries.

# http://http://macscripter.net/post.php?tid=45689
# 
# 2017/05/03 -- enhanced the code building the list of names and the list of duplicate files

use AppleScript version "2.4"
use framework "Foundation"
use scripting additions

on run
	(*
	# Don't worry, Macintosh HD is not my boot disk
	set largeFolder to POSIX path of ("Macintosh HD:Users:Important:pour ebay:")
	set smallFolder to POSIX path of ((path to desktop as text) & "_X28737Æ’YK:")
	*)
	set largeFolder to POSIX path of (choose folder with prompt "Choose the folder containing numerous files !")
	set smallFolder to POSIX path of (choose folder with prompt "Choose the folder containing few files !")
	
	my compare(largeFolder, smallFolder)
end run

on compare(largeFolder, smallFolder)
	local iconp2d, name, storageName, storageFolder, localisedLabels, theLabel, largeDirectoryContents, smallDirectoryContents, namesInSmall, anURL, itsName
	set iconName to "Icon" & return
	set p2d to path to desktop
	set storageName to "duplicates_setacilpud"
	# Create the folder to store duplicates.
	set storageFolder to my createFolderNamed:storageName inFolder:(POSIX path of p2d)
	# We must pass a localized label name so we grab them.
	set localisedLabels to getLabelsNames()
	--> {"Aucun", "Gris", "Vert", "Violet", "Bleu", "Jaune", "Rouge", "Orange"
	# We will use red, why not ?
	set theLabel to localisedLabels's item 7
	# Grab the entire contents of the main folder
	set largeDirectoryContents to my getEntireContents:largeFolder
	# Grab the entire contents of the small folder
	set smallDirectoryContents to my getEntireContents:smallFolder
	# Build an array containing the names of files of small folder
	set namesInSmall to current application's NSMutableArray's arrayWithArray:{}
	repeat with anURL in smallDirectoryContents
		set itsName to anURL's |lastPathComponent|()
		(namesInSmall's insertObject:itsName atIndex:(count namesInSmall))
	end repeat
	set thePredicate to current application's class "NSPredicate"'s predicateWithFormat:("lastPathComponent IN %@") argumentArray:({namesInSmall})
	# Build a list of files of large folder whose name is available in the small one
	set theDuplicates to (largeDirectoryContents's filteredArrayUsingPredicate:(thePredicate))
	
	# Main loop
	repeat with anURL in theDuplicates
		--set anURL to contents of anURL
		# If the file is a duplicate, apply the wanted treatment.
		set itsName to (anURL's |lastPathComponent|()) as text
		if (itsName does not start with ".") and itsName is not iconName then
			# May set a red label on duplicates
			--(my setTags:{theLabel} forItem:(anURL's |path|() as text))
			# May copy duplicates into the storage folder. I do that because I don't wish to modify my largeFolder
			(my copyThis:(anURL's |path|() as text) intoFolder:storageFolder)
			# Move duplicates into the storage folder. I guess that it's the one you will use.
			--(my movePath:(anURL's |path|() as text) toFolder:storageFolder)
		end if
	end repeat
end compare


#=====#=====#=====#=====#=====#=====

# Some ASObjC handlers borrowed to Shane STANLEY

#=====#=====#=====#=====#=====#=====

on getEntireContents:POSIXPath
	set |⌘| to current application
	-- Set up an NSFileManager enumerator and get the folder's "entire contents" (visible files and folders) as NSURLs.
	set theFileManager to |⌘|'s class "NSFileManager"'s defaultManager()
	set rootURL to |⌘|'s class "NSURL"'s fileURLWithPath:(POSIXPath)
	set dirAndPackageKeys to |⌘|'s class "NSArray"'s arrayWithArray:({|⌘|'s NSURLIsDirectoryKey, |⌘|'s NSURLIsPackageKey})
	set theEnumerator to theFileManager's enumeratorAtURL:(rootURL) includingPropertiesForKeys:(dirAndPackageKeys) options:((|⌘|'s NSDirectoryEnumerationSkipsHiddenFiles) + (|⌘|'s NSDirectoryEnumerationSkipsPackageDescendants as integer)) errorHandler:(missing value)
	set entireContents to theEnumerator's allObjects()
	return entireContents
end getEntireContents:

#=====#=====#=====#=====#=====#=====

-- Creates a new folder. There is no error if the folder already exists, and it will also create intermediate folders if required
on createFolderNamed:proposedName inFolder:POSIXPath # appelé par une instruction
	local |⌘|, theFolderURL, theDestURL, theFileManager
	
	set |⌘| to current application
	set theFolderURL to |⌘|'s |NSURL|'s fileURLWithPath:POSIXPath
	if class of proposedName is text then set proposedName to |⌘|'s NSString's stringWithString:proposedName
	set proposedName to proposedName's stringByReplacingOccurrencesOfString:"/" withString:":"
	set theDestURL to theFolderURL's URLByAppendingPathComponent:proposedName
	set theFileManager to |⌘|'s NSFileManager's |defaultManager|()
	-- set {theResult, theError} to theFileManager's createDirectoryAtURL:theDestURL withIntermediateDirectories:true attributes:(missing value) |error|:(reference)
	theFileManager's createDirectoryAtURL:theDestURL withIntermediateDirectories:true attributes:(missing value) |error|:(missing value)
	-- if not (theResult as boolean) then error (theError's |localizedDescription|() as text)
	
	return theDestURL's |path| as text
end createFolderNamed:inFolder:

#=====#=====#=====#=====#=====#=====

on movePath:posixSource toFolder:posixDestination
	local |⌘|, theSourceURL, destURL, shortURL, origName, theFileManager, theResult, theError, destName
	
	set |⌘| to current application
	set theSourceURL to |⌘|'s |NSURL|'s fileURLWithPath:posixSource
	set destURL to |⌘|'s |NSURL|'s fileURLWithPath:posixDestination
	set theFileManager to |⌘|'s NSFileManager's |defaultManager|()
	set {theResult, theError} to theFileManager's createDirectoryAtURL:destURL withIntermediateDirectories:true attributes:(missing value) |error|:(reference)
	--if not (theResult as boolean) then error (theError's |localizedDescription|() as text)
	# maintenant, move cheminPosixDuFichierSource item
	set destName to theSourceURL's |lastPathComponent|()
	set destURL to destURL's URLByAppendingPathComponent:destName
	my moveFromURL:theSourceURL toURL:destURL withReplacing:true
	return destURL's |path| as text
	
end movePath:toFolder:

#=====

-- This handler is called by other handlers, and is not meant to called directly
on moveFromURL:sourceURL toURL:destinationURL withReplacing:replaceFlag
	set |⌘| to current application
	set theFileManager to |⌘|'s NSFileManager's |defaultManager|()
	set {theResult, theError} to (theFileManager's moveItemAtURL:sourceURL toURL:destinationURL |error|:(reference))
	if not theResult as boolean then
		if replaceFlag and (theError's code() = |⌘|'s NSFileWriteFileExistsError) then -- it already exists, so try replacing
			-- replace existing file with temp file atomically, then delete temp directory
			set {theResult, theError} to theFileManager's replaceItemAtURL:destinationURL withItemAtURL:sourceURL backupItemName:(missing value) options:(|⌘|'s NSFileManagerItemReplacementUsingNewMetadataOnly) resultingItemURL:(missing value) |error|:(reference)
			-- if replacement failed, return error
			if not theResult as boolean then error (theError's |localizedDescription|() as text)
		else -- replaceFlag is false or an error other than file already exists, so return error
			error (theError's |localizedDescription|() as text)
		end if
	end if
end moveFromURL:toURL:withReplacing:

#=====#=====#=====#=====#=====#=====

on copyThis:POSIXPath intoFolder:folderOrPath
	local |⌘|, theSourceURL, destName, theFolderURL, theDestURL
	
	set |⌘| to current application
	set theSourceURL to |⌘|'s |NSURL|'s fileURLWithPath:POSIXPath
	set destName to theSourceURL's |lastPathComponent|()
	set theFolderURL to |⌘|'s |NSURL|'s fileURLWithPath:folderOrPath
	set theDestURL to theFolderURL's URLByAppendingPathComponent:destName
	my copyFromURL:theSourceURL toURL:theDestURL withReplacing:true
	
end copyThis:intoFolder:

#=====

-- This handler is called by other handlers, and is not meant to called directly
on copyFromURL:sourceURL toURL:destinationURL withReplacing:replaceFlag
	set |⌘| to current application
	set theFileManager to |⌘|'s NSFileManager's |defaultManager|()
	set {theResult, theError} to (theFileManager's copyItemAtURL:sourceURL toURL:destinationURL |error|:(reference))
	if not theResult as boolean then
		if replaceFlag and (theError's code() as integer = |⌘|'s NSFileWriteFileExistsError as integer) then -- it already exists, so try replacing
			-- create suitable temporary directory in destinationURL's parent folder
			set {tempFolderURL, theError} to theFileManager's URLForDirectory:(|⌘|'s NSItemReplacementDirectory) inDomain:(|⌘|'s NSUserDomainMask) appropriateForURL:(destinationURL's |URLByDeletingLastPathComponent|()) create:true |error|:(reference)
			if tempFolderURL = missing value then error (theError's |localizedDescription|() as text) -- failed, so return error
			-- copy sourceURL to temp directory
			set tempDestURL to tempFolderURL's URLByAppendingPathComponent:(destinationURL's |lastPathComponent|())
			set {theResult, theError} to theFileManager's copyItemAtURL:sourceURL toURL:tempDestURL |error|:(reference)
			if not theResult as boolean then
				-- copy failed, so delete temporary directory and return error
				theFileManager's removeItemAtURL:tempFolderURL |error|:(missing value)
				error (theError's |localizedDescription|() as text)
			end if
			-- replace existing file with temp file atomically, then delete temp directory
			set {theResult, theError} to theFileManager's replaceItemAtURL:destinationURL withItemAtURL:tempDestURL backupItemName:(missing value) options:(|⌘|'s NSFileManagerItemReplacementUsingNewMetadataOnly) resultingItemURL:(missing value) |error|:(reference)
			theFileManager's removeItemAtURL:tempFolderURL |error|:(missing value)
			-- if replacement failed, return error
			if not theResult as boolean then error (theError's |localizedDescription|() as text)
		else -- replaceFlag is false or an error other than file already exists, so return error
			error (theError's |localizedDescription|() as text)
		end if
	end if
end copyFromURL:toURL:withReplacing:

#=====#=====#=====#=====#=====#=====

on setTags:tagList forItem:fileOrPOSIXPath
	local |⌘|, thisURL, theResult, theError
	
	set |⌘| to current application
	if class of fileOrPOSIXPath is not text then set fileOrPOSIXPath to POSIX path of fileOrPOSIXPath
	set thisURL to |⌘|'s class "NSURL"'s fileURLWithPath:fileOrPOSIXPath -- make URL
	set {theResult, theError} to thisURL's setResourceValue:tagList forKey:(|⌘|'s NSURLTagNamesKey) |error|:(reference)
	if theResult as boolean is false then error (theError's |localizedDescription|() as text)
	
end setTags:forItem:

#=====#=====#=====#=====#=====#=====

on getLabelsNames()
	
	return (current application's NSWorkspace's sharedWorkspace()'s fileLabels()) as list
	--> {"Aucun", "Gris", "Vert", "Violet", "Bleu", "Jaune", "Rouge", "Orange"}
end getLabelsNames

#=====#=====#=====#=====#=====#=====

Yvan KOENIG running Sierra 10.12.4 in French (VALLAURIS, France) mardi 2 mai 2017 21:59:29

Hi.

Since you’re running Mac OS 11.6, this should be pretty quick at returning a list of aliases to the items in folder A whose names don’t occur in folder B. I’ve left it at that, as that’s as far as your question goes.

-- This script assumes that the folders contain only files (no subfolders).

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

set |⌘| to current application
set folderA to |⌘|'s class "NSURL"'s fileURLWithPath:("/Volumes/External HD 01/Works-In-Progress/Superman (Ext HD)")
set folderB to |⌘|'s class "NSURL"'s fileURLWithPath:("/Users/Dave/Dropbox/Old Time Radio Library (Dropbox)/Superman (OTR)")

set fileManager to |⌘|'s class "NSFileManager"'s defaultManager()
set folderAContents to fileManager's contentsOfDirectoryAtURL:(folderA) includingPropertiesForKeys:({}) options:(|⌘|'s NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
set folderBContents to fileManager's contentsOfDirectoryAtURL:(folderB) includingPropertiesForKeys:({}) options:(|⌘|'s NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)

set nameNotInFolderB to |⌘|'s class "NSPredicate"'s predicateWithFormat:("NOT lastPathComponent IN %@") argumentArray:({folderBContents's valueForKey:("lastPathComponent")})
set untreatedItems to (folderAContents's filteredArrayUsingPredicate:(nameNotInFolderB)) as list

Only the names are needed from folder B, so the code could be simplified a little as below, although the above’s inexplicably slightly faster in my tests this evening:

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

set |⌘| to current application
set folderA to |⌘|'s class "NSURL"'s fileURLWithPath:("/Volumes/External HD 01/Works-In-Progress/Superman (Ext HD)")
set folderBPath to "/Users/Dave/Dropbox/Old Time Radio Library (Dropbox)/Superman (OTR)"

set fileManager to |⌘|'s class "NSFileManager"'s defaultManager()
set folderAContents to fileManager's contentsOfDirectoryAtURL:(folderA) includingPropertiesForKeys:({}) options:(|⌘|'s NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
set folderBContentNames to fileManager's contentsOfDirectoryAtPath:(folderBPath) |error|:(missing value)

set nameNotInFolderB to |⌘|'s class "NSPredicate"'s predicateWithFormat:("NOT lastPathComponent IN %@") argumentArray:({folderBContentNames})
set untreatedItems to (folderAContents's filteredArrayUsingPredicate:(nameNotInFolderB)) as list

t.spoon: The situation has changed and I’ll spell it out here .

Because I have a ton of downloaded media files (all domain-free), a lot of my Mac HD is used. Basically I (now) want to use my External HD for the storage of the actual files (“live files”) with corresponding aliases in my Mac HD.

I had taken all the Old Time Radio (OTR) mp3 files for one specific program (on my Ext HD) and created aliases of each. These aliases were then placed on my Mac HD. Today, I find there is one more alias than a corresponding mp3 file (1,113 mp3 and 1,114 alias files).

This may be that there are two aliases for the same mp3 file, or one mp3 file was accidentally deleted. In either case I need to know. I can come up with an easy fix either way, once I find that malcontented alias file. Ergo, two variables (all in Applescript) containing the mp3 and the alias files and have Applescript come up with the odd one out. It is the comparing that has me in a funk right now.

No offense to anyone else who answered me, but there has to be a simple Applescript solution to this. It is no problem making the two lists of files as variables, but to look for that one extra alias file is my problem.

(Sorry about the length of this message.)

BG