Finding the file with the most recent "date modified"

Thanks for the confirmation and explanation Stefan.
Following one of your posts ages ago I tend to always throw in as alias list when getting a file list like that.

Thanks

It seems that I must understand that NSDirectoryEnumerationSkipsHiddenFile filter files whose name starts with “.” even when they aren’t hidden. It’s not the Finder’s behavior.
On my mac, “hidden files” are always visible so I took care of .DS_Store in my original answer.
Maybe it would be better to filter every file whose name starts with a dot.

Yvan KOENIG running Yosemite 10.10.4 in French (VALLAURIS, France) jeudi 13 août 2015 12:25:31

To make it clear this is the behavior of the usual ways to list files in the file system.

System Events displays always all files including hidden files.
Cocoa displays all files including hidden files unless you set the option NSDirectoryEnumerationSkipsHiddenFiles
Finder displays hidden files depending on the preference key AppleShowAllFiles

System Events and Cocoa don’t consider the Finder preference at all.

I guess that 95% of the users have AppleShowAllFiles set to the default value, false.

Not at all. Putting it in, the ASObjC version is only about 16 times faster, compared with 40 times faster otherwise.

As Stefan said, the Finder takes the Finder’s settings into account. I don’t think anything else does; for example, choose file has its own parameter for whether to include invisible files.

A minor thing: NSDirectoryEnumerationSkipsHiddenFiles skips files where the name begins with “.”, plus files that have the hidden attribute (NSURLIsHiddenKey) set.

Better still, put it in a library. But I think there’s still a strong perception that more code = slower. Particularly in a language with the performance peculiarities of AppleScript, there’s rarely much correlation.

Oh very good. That’s what I was looking for. I knew there was a way to target it.

Cheers. Works like a charm.

Out of interest try this variation to see a speed jump.


set theFolder to (choose folder) as text

tell application "Finder"
   set the_file to last item of (sort (get files of folder theFolder as alias list) by modification date)
end tell

It’s a little trick I learnt off Stefan.

Is it faster than :

set theFolder to (choose folder)

tell application "Finder"
	set the_file to last item of (sort (get files of theFolder as alias list) by modification date)
end tell

If you are interested by speed, I just wish to say that Shane Stanley gave a code which is 14 times faster than the “optimized” Finder version (40 times faster than the “non optimized” one).
It’s not because a script listing is shorter than an other one that it is faster.

Yvan KOENIG running Yosemite 10.10.5 in French (VALLAURIS, France) lundi 17 août 2015 11:01:04

Thanks Yvan.
Yeah, I understand it’s not because the code listing is shorter :wink:

Do we need ASObjC Runner present to get Shane’s code to work? I could compile it but when executed I got an error.

TIA

No need for ASObjC Runner, just running under Yosemite (or El Capitan)

Are you sure that you inserted the three instructions available at the beginning of Shane’s proposal ?
I ask that because the script compiles without them but of course it fails.
Without the 3 instructions the error message would be : class “NSURL” ne comprend pas le message « fileURLWithPath_ ».

Yvan KOENIG running Yosemite 10.10.5 in French (VALLAURIS, France) lundi 17 août 2015 12:05:07

P.S. In fact, we may also use it under Mavericks but in this case, the handler must be saved inas a library which must be “blessed” and stored in a dedicated location :
“/Library/Script Libraries/”
or
“~/Library/Script Libraries/”

Thanks for the info Yvan, think you may have nailed it in your P.S.

I’m on Mavericks, and I was getting the error ‘Can’t find framework “Foundation” of <>. Access not allowed.’

Please can you explain what you mean by ‘blessed’? Are you just talking about access privileges?

TIA

TecNik,

This gives a good explanation with screen shots:

macosxautomation.com/mavericks/libraries/asoc.html

There’s also a more detailed explanation here:

macscripter.net/viewtopic.php?id=41638

OK, I am not a sooth sayer so I can’t guess which operating system is used :wink:

Open the script in Script Editor
Save it as a “paquet de scripts” (I don’t know the exact wording in English)
On the top/right of the script’s window you will see an icon entitled “Contenu du paquet” (one more time, I don’t know the exact wording in English)
Click on it to reveal a pane which will appear on the right edge of the window.
It contain a checkbox entitled “AppleScript/Bibliothèque Objective-C” (I assume that it’s “AppleScript/Library Objective-C” in English)
Check it and resave the file.
Store it in one of the folder described in my late message.

After that, assuming that you named the library as mostRecentlyModifiedIemIn.scptd, you will be able to call it with :

use AppleScript version "2.3"
use theLib : script "mostRecentlyModifiedIemIn"
use scripting additions
use framework "Foundation"

set folderPosixPath to POSIX path of (choose folder)
set mostRecentlyModifiedIemIn to theLib's mostRecentlyModifiedIemIn:(folderPosixPath)

Yvan KOENIG running Yosemite 10.10.5 in French (VALLAURIS, France) lundi 17 août 2015 14:45:15

Hi Shane,

Thanks for the links.
I’ll have a dabble when I get chance, looks interesting.

:slight_smile:

I needed a quicker way than Finder to sort a folder’s files by modification date, which is why I found this thread. The folder has 400 files, and Finder varies, taking from about 6 to 11 seconds to sort it by mod date (“tell application ‘Finder’ to sort targetFolder by modification date”). Shane’s handler on this page, which I don’t understand, when modified for my needs (shown here), takes only about 3.5 seconds every time, to return a complete list of the 400 filenames. (I’m using a 2020 Macbook Air running Catalina). My way of doing this, as seen here, may be clumsy, but I don’t know enough to do any better. If anyone needs a list of all filenames in a folder sorted by modification date by Shane’s brilliant handler, this works better than using Finder. Thanks go to Shane.
–●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●
–●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●
–Shane’s original coding (on this page) modified to return a list of filenames sorted in mod order. Cut and paste everything below this line into Script Editor to use.
–●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●
global countOfFolderPosixFile --the only way I know to get this variable into Shane’s handler (el7)
–●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●
–Shane’s coding
use AppleScript version “2.7”
use scripting additions
use framework “Foundation”
–●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●
set folderPosixPath to POSIX path of (choose folder) --file path for Shane’s handler below
set folderPosixFile to ((folderPosixPath as POSIX file) as string) --Mac file path for chosen folder (el7)
tell application “Finder” to set countOfFolderPosixFile to (count of (name of every file of (folderPosixFile as alias) as list)) as integer --# of files in chosen folder (el7)
–●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●
–result of Shane’s handler starts with “its”. “its” seems both to invoke the handler and return its result, whereas in Applescript, with which I’m familiar, i’ve been doing the same thing the following way: “set myVariable to myHandler()” (el7 – hope I’m accurate about this)
set sortedFileNames to its mostRecentlyModifiedIemIn:folderPosixPath
–●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●
–returns a carriage-return-delimited string of the list sorted paths returned by Shane’s handler (el7)
set AppleScript’s text item delimiters to (character id 10)
set sortedFileNames to text items of sortedFileNames as string
–●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●
set stripFilePaths to (character id 10) & ((folderPosixPath as POSIX file) as string) --creating delimiter for applescript string, which sortedFileNames is now (el7)
–●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●
–strips out all, except the first, path strings in sortedFileNames string (el7)
set AppleScript’s text item delimiters to stripFilePaths
set sortedFileNames to text items of sortedFileNames as list
–●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●
set AppleScript’s text item delimiters to “”
tell application “Finder” to set item 1 of sortedFileNames to name of (item 1 of sortedFileNames as alias) --strips out the first and only path string remaining in sortedFileNames (el7)
–●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●
sortedFileNames --the MdGuffin, a list of file names of the chosen folder in descending modification date order, sorted by Shane’s handler (el7)
–●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●
–Shane’s handler, mostly, with his notes, which are about the only things i understand (el7)
on mostRecentlyModifiedIemIn:folderPosixPath
set theNSURL to current application’s class “NSURL”'s fileURLWithPath:folderPosixPath – make URL for folder because that’s what’s needed
set theNSFileManager to current application’s NSFileManager’s defaultManager() – get file manager
– get contents of the folder
set keysToRequest to {current application’s NSURLPathKey, current application’s NSURLContentModificationDateKey} – keys for values we want for each item
set theURLs to theNSFileManager’s contentsOfDirectoryAtURL:theNSURL includingPropertiesForKeys:keysToRequest options:(current application’s NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
– get mod dates and paths for URLs
set theInfoNSMutableArray to current application’s NSMutableArray’s array() – array to store new values in
repeat with i from 1 to theURLs’s |count|()
set anNSURL to (theURLs’s objectAtIndex:(i - 1)) – zero-based
(theInfoNSMutableArray’s addObject:(anNSURL’s resourceValuesForKeys:keysToRequest |error|:(missing value))) – get values dictionary and add to array
end repeat
– sort them in date order
set theNSSortDescriptor to current application’s NSSortDescriptor’s sortDescriptorWithKey:(current application’s NSURLContentModificationDateKey) ascending:false – describes the sort to perform
theInfoNSMutableArray’s sortUsingDescriptors:{theNSSortDescriptor} – do the sort
–●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●
–build a list sorted by modification date by Shane’s handler, using AppleScript lingo with a key line of Shane’s (el7)
tell application “Finder”
set sortedFileNames to {} as list
repeat with i from 0 to countOfFolderPosixFile
((theInfoNSMutableArray’s objectAtIndex:(i))'s valueForKey:(current application’s NSURLPathKey)) as text – extract path and convert to text–Shane’s important line of code, with his “(0)'s” changed to my “(i)'s” in a repeat loop (el7)
set sortedFileNames to sortedFileNames & ((result as POSIX file) as string) as list
end repeat
return sortedFileNames --right here, all the heavy lifting is done (el7)
end tell
end mostRecentlyModifiedIemIn:
–end of modified coding
–●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●
–●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Model: MacBook Air 2020, OS 10.15.7
AppleScript: 2.7
Browser: Safari
Operating System: Other

For a list in modification order, use this code. And don’t put it inside a Finder tell block.

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

set folderPosixPath to POSIX path of (choose folder)
its filesSortedByModDate:folderPosixPath

on filesSortedByModDate:folderPosixPath
	set theNSURL to current application's class "NSURL"'s fileURLWithPath:folderPosixPath -- make URL for folder because that's what's needed
	set theNSFileManager to current application's NSFileManager's defaultManager() -- get file manager
	-- get contents of the folder
	set keysToRequest to {current application's NSURLPathKey, current application's NSURLContentModificationDateKey} -- keys for values we want for each item
	set theURLs to theNSFileManager's contentsOfDirectoryAtURL:theNSURL includingPropertiesForKeys:keysToRequest options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
	-- get mod dates and paths for URLs
	set theInfoNSMutableArray to current application's NSMutableArray's array() -- array to store new values in
	repeat with oneURL in theURLs
		(theInfoNSMutableArray's addObject:(oneURL's resourceValuesForKeys:keysToRequest |error|:(missing value))) -- get values dictionary and add to array
	end repeat
	-- sort them in date order
	set theNSSortDescriptor to current application's NSSortDescriptor's sortDescriptorWithKey:(current application's NSURLContentModificationDateKey) ascending:false -- describes the sort to perform
	theInfoNSMutableArray's sortUsingDescriptors:{theNSSortDescriptor} -- do the sort
	-- get the path of the first item
	return (theInfoNSMutableArray's valueForKey:(current application's NSURLPathKey)) as list
end filesSortedByModDate:

You can take out the expermental (and apparently offensive) Finder tell block, and it still gives a result I need – to return not an empty list or a single file, but a list of just the names of all the files of a folder, sorted in mod date order. I don’t know if anyone needs to do this, but i posted it here just in case, because I couldn’t find a way to do it anywhere else, except with Finder, which is too slow for my needs. I needed this for a “choose from list” of these filenames, where the path of them already is known, and all those redundant paths obfuscate the necessary info.

It’s not offensive – it’s just that there’s a common misconception that because you’re dealing in files, you somehow have to involve the Finder, whereas often doing so is counter-productive. As a general rule, it’s a good aim to have only code relevant to an application inside that app’s tell block.

Just for fun, I created a whole vanilla AppleScript with a combSort

It get a folder with ~200 files using “System Events” instead of Finder and sorts it in .12 seconds

use AppleScript version "2.3"
use scripting additions

set theFolder to (choose folder) as text
tell application "System Events" --to tell folder theFolder
	set theFolder to folder theFolder
	set fileList to {path, modification date} of every file of theFolder --by modification date
end tell
set sortedList to {}
repeat with i from 1 to count (item 1 of fileList)
	set end of sortedList to {item i of item 1 of fileList, item i of item 2 of fileList}
end repeat
combSort(sortedList, 2)
get first item of sortedList

on combSort(nlist as list, sortitem)
	local i, j, cc, ns, gap, sf, sw -- ns means No Swap
	script M
		property alist : nlist
	end script
	set sf to 1.7 -- 1.3
	set cc to (count of M's alist)
	set gap to cc div sf
	repeat until gap = 0
		set ns to true
		repeat with i from 1 to (cc - gap)
			if (item sortitem of item i of M's alist) > (item sortitem of item (i + gap) of M's alist) then
				set sw to (item i of M's alist)
				set (item i of M's alist) to (item (i + gap) of M's alist)
				set (item (i + gap) of M's alist) to sw
				set ns to false
			end if
		end repeat
		if ns then set gap to gap div sf
	end repeat
end combSort