Hi,
I’ve been battling with some code for the past two days.
I have a folder filled with files. I want to present the user with the option to chose files that begin with “2013” from within the folder.
I can successfully create a list of all the files in the folder.
I can successfully create a list of the names of all the files in the folder that begin with “2013”.
I can successfully get the user’s choices from said list of names.
What I can do is match the names to the files.
Equating them results in a false result even though the texts being compared seem to be identical. Any Ideas?
on run
set _path to choose folder
getFiles(_path)
end run
on getFiles(_path) --path correctly directs to a specific folder
tell application "Finder"
set _files to files of _path whose name starts with "2013" --sets _files to a list of some of the files in the folder _path
set _fileNames to {} --defines _fileNames as an empty list
set _chosenFiles to {} --defines _chosenFiles as an empty list
repeat with _file in _files
set end of _fileNames to name of _file
end repeat
--_fileNames is now a list of all the names of the files in the _files
set _chosenFileNames to choose from list _fileNames with multiple selections allowed
--_chosenFileNames is now a list of the selected names of files
repeat with _chosenFileName in _chosenFileNames
repeat with _file in _files
if _chosenFileName is name of _file then set end of _chosenFiles to _file
--for some reason _chosenFileName never equals (name of _file)
end repeat
end repeat
--chosenFiles should now be a list of files (not names) whose names were chosen
end tell
end getFiles
Browser: Safari 537.36
Operating System: Mac OS X (10.8)
If I understand well, the path to every selected files is :
(_path as text) & fileName
or
(_path as text) & “:” & fileName
Test once if (_path as text) ends with “:” to decide which formula must be used.
KOENIG Yvan (VALLAURIS, France) mardi 9 juillet 2013 16:45:17
Oops I forgot that _path is grabbed by choose folder.
Taking care of that we know that it ends with “:”
Here is an edited version doing the trick.
on run
set _path to choose folder
# _path as text ends with ":"
getFiles(_path)
result
end run
on getFiles(_path) --path correctly directs to a specific folder
tell application "Finder"
set _fileNames to name of files of _path whose name starts with "2013" --sets _files to a list of some of the files in the folder _path
set _chosenFiles to {} --defines _chosenFiles as an empty list
--_fileNames is now a list of all the names of the files in the _files
set _chosenFileNames to choose from list _fileNames with multiple selections allowed
--_chosenFileNames is now a list of the selected names of files
repeat with _chosenFileName in _chosenFileNames
# set end of _chosenFiles to (_path as text) & _chosenFileName
# set end of _chosenFiles to file ((_path as text) & _chosenFileName)
# set end of _chosenFiles to alias ((_path as text) & _chosenFileName)
set end of _chosenFiles to file _chosenFileName of _path
end repeat
--chosenFiles should now be a list of files (not names) whose names were chosen
end tell
return _chosenFiles
end getFiles
this script retrieves only the file names and later get the file references with the whose filter
on getFiles(_path) --path correctly directs to a specific folder
tell application "Finder"
set _fileNames to name of files of _path whose name starts with "2013" -- sets _files to a list of some of the files in the folder _path
end tell
set _chosenFiles to {} --defines _chosenFiles as an empty list
--_fileNames is now a list of all the names of the files in the _files
set _chosenFileNames to choose from list _fileNames with multiple selections allowed
--_chosenFileNames is now a list of the selected names of files
if _chosenFileNames is false then return
repeat with _chosenFileName in _chosenFileNames
tell application "Finder" to set end of _chosenFiles to file _chosenFileName of _path
end repeat
return _chosenFiles
--chosenFiles should now be a list of files (not names) whose names were chosen
end getFiles
It seems to me that it would be better to get the references first. Then you can get the names through the references. You can’t get the references directly from the names. Just thinking about avoiding calls to Finder with the every element reference form. Am I thinking wrongly?
From the point of view of speed ” especially if there are a lot of files ” it’s better for the Finder to return all the names at once (in a single command) than for it to put together and return references and then have to extract the name from each reference in turn in a repeat.
Faster still would be to get the names of all the files in the folder, without using a ‘whose’ filter, and then use vanilla AppleScript in a repeat to parse the list for names beginning with “2013”.
As you can see from Yvan’s script (in the edit which exists as I post this), you can make a reference from a name by placing ‘file’ and the name and ‘of’ in front of a reference to the folder.
Thanks for the replies everyone, very informative!
I changed my code to include your suggestions and also avoided having my Finder run a “whose” command through the _path folder, as it was slowing the script significantly (a few thousand files…)
I still have an issue with opening files from the list. I’d appreciate your input.
on getFiles(_path)
tell application "Finder" to set _fileNames to name of files of _path --
set _matchingNames to {} --defines _matchingNames as an empty list
repeat with _fileName in _fileNames
if _fileName starts with "2013" then set end of _matchingNames to _fileName
end repeat
set _chosenFiles to {} --defines _chosenFiles as an empty list
set _chosenFileNames to choose from list _matchingNames with prompt "Which file(s) would you like to open?" with title "Choose Files" OK button name "Open File(s)" with multiple selections allowed --_chosenFileNames is now a list of the selected names of files
if _chosenFileNames is false then return
repeat with _chosenFileName in _chosenFileNames
tell application "Finder" to set end of _chosenFiles to (1st file of _path whose name is _chosenFileName)
--tell application "Finder" to open (1st file of _path whose name is _chosenFileName) --running this line opens the files successfully, but I rather have them as a list that I can access at will.
end repeat
repeat with _chosenFile in _chosenFiles
tell application "Finder" to open file _chosenFile --results in an error: Can't make document [file] into type integer.
end repeat
return _chosenFiles
end getFiles
I’d open and save the file reference in the same repeat loop
on getFiles(_path)
tell application "Finder" to set _fileNames to name of files of _path --
set _matchingNames to {} --defines _matchingNames as an empty list
repeat with _fileName in _fileNames
if _fileName starts with "2013" then set end of _matchingNames to _fileName
end repeat
set _chosenFiles to {} --defines _chosenFiles as an empty list
set _chosenFileNames to choose from list _matchingNames with prompt "Which file(s) would you like to open?" with title "Choose Files" OK button name "Open File(s)" with multiple selections allowed --_chosenFileNames is now a list of the selected names of files
if _chosenFileNames is false then return
repeat with _chosenFileName in _chosenFileNames
tell application "Finder"
set _chosenFile to file _chosenFileName of _path
open _chosenFile
end tell
set end of _chosenFiles to _chosenFile
end repeat
return _chosenFiles
end getFiles
Hi Stefan,
Thanks for the quick reply. as you can see in my latest code, I already had the option you mentioned.
I can open the files directly from the loop (as you did), but I want to save them as a list and them open them at a later point in my code.
The last repeat loop in my code throws an erro when I try and open files from the _chosenFiles list. I probably need to save the items into the list as aliases or some other type of data, but I can’t seem to get it to work.
This is more in line with Nigel Garvey’s solution, in addition, I do as Christopher Stone, and creates an alias list, as that is the fastest, for further processing.
I use some handlers here, one new: I quicksort an alias list. the result is a list of aliases that can be processed further. The _filter handler, filters a list, it takes a handler as a parameter, this time, the handler a match is adjusted to coerce an alias into text, -as the quicksort handler. I tested it on a folder with 397 files, 99 for 2013, and it used 7.82 seconds.
The caveat is that it can’t be any 2013 in folder names when you use this.
Further processing of the list of aliases should be straight forward.
Further optimization could be to coerce every item of the list into a text
I rewrote my proposal to drop Finder and speak to System Events.
Here it’s dramatically faster.
alias "Macintosh HD:Users:yvankoenig:Documents:tempo:"
getFiles(result)
result
on getFiles(_path) --path correctly directs to a specific folder
tell application "System Events"
set _fileNames to name of files of _path whose name starts with "2013"
end tell
set _chosenFiles to {} --defines _chosenFiles as an empty list
--_fileNames is now a list of all the names of the files in the _files
set _chosenFileNames to choose from list _fileNames with multiple selections allowed
--_chosenFileNames is now a list of the selected names of files
tell application "System Events"
repeat with _chosenFileName in _chosenFileNames
# set end of _chosenFiles to (_path as text) & _chosenFileName
# set end of _chosenFiles to file ((_path as text) & _chosenFileName)
# set end of _chosenFiles to alias ((_path as text) & _chosenFileName)
set end of _chosenFiles to file _chosenFileName of _path
end repeat
end tell
--chosenFiles should now be a list of files (not names) whose names were chosen
return _chosenFiles
end getFiles
KOENIG Yvan (VALLAURIS, France) mercredi 10 juillet 2013 16:53:56
It is hard to write a handler that returns a range of items in a sorted list, with binary properties, with regards to speed. It just occured to me, that I only have to find the first item, and then go for a convention of retesting the item. If I can do that, then the solution will take around a second on my machine, in comparison to my former result of 7.82 secs.
Edit
In order to write something like bsearch, that returns a range, I’ll have to coerce the alias list to a text list, which is done in a whiff, and having done that, then I may as well use sed/grep to get the ranges of names, so I don’t even bother to post it.
Here is a version with grep, that seem to work fast enough ( I have tested it with 800 files. )
script o
property l : {}
property m : {}
property n : {}
end script
tell application "Finder"
set file_list to every file of folder (choose folder) as alias list
end tell
set listCount to length of file_list
set {o's l, i} to {file_list, 1}
repeat listCount times
set end of o's m to item i of o's l as text
set i to i + 1
end repeat
set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, linefeed}
set theText to o's l as text
set AppleScript's text item delimiters to tids
set o's m to paragraphs of (do shell script "grep '2013[^:]*$' <<<" & quoted form of theText & "sort ")
set o's n to paragraphs of (do shell script "grep -o '2013[^:]*$' <<<" & quoted form of theText & "sort ")
set {listCount, o's l, i} to {count o's m, {}, 1}
repeat listCount times
set end of o's l to item i of o's m as alias
set i to i + 1
end repeat
tell current application
activate
set selectedFiles to (choose from list o's n with multiple selections allowed)
end tell
if selectedFiles is false then error number -128
set idlist to {}
# tell application "Finder" or "System Events"
repeat with aFile in selectedFiles
set end of idlist to indexOfItem(aFile, o's n)
# or do an op with item (indexOfItem(aFile, o's n) o's l here..
end repeat
# end tell
on indexOfItem(theItem, theList) -- Emmanuel Levy
set text item delimiters to return
set theList to return & theList & return
set text item delimiters to {""}
try
-1 + (count (paragraphs of (text 1 thru (offset of (return & theItem & return) in theList) of theList)))
on error
0
end try
end indexOfItem
indexOfItem is overkill, with given folder path and file name you can create the alias reference very simple
set baseFolderPath to (choose folder) as text
tell application "Finder"
set file_list to every file of folder baseFolderPath -- as alias list is not needed, Finder specifier can be coerced to text without a Finder tell block
end tell
-- .
repeat with aFile in selectedFiles
set end of idlist to alias (baseFolderPath & aFile)
end repeat