Copy every nth file from a folder to a new folder

Hy everyone,

I want to move every 10th file from a folder to a new folder. I found this script and tried it, but nothing happens…
This is the script:

set i to 0
set theFolder to (choose folder)
tell application "Finder"
	set savedFiles to (make new folder at desktop with properties {name:"Result"})
	repeat
		try
			move item ((i * 3) + 1) of (files of theFolder) to savedFiles
		on error
			exit repeat
		end try
		set i to i + 1
	end repeat
end tell

Can you help me please?
Thanks a lot

Kind Regards

Hi.

  1. “every 10th file from a folder” is a vague concept from the point-of-view of defining which files are actually moved. Is a more-or-less random selection what you want?

  2. When the ‘nth’ file is removed from a folder, the files numbered higher than n are all re-indexed to close the gap. So the file which was the (n + 1)th is now the nth and the file which is now the (2 * n)th was originally the (2 * n + 1)th. So the files get out of sync with the repeat loop. It’s best to get the files as a fixed AppleScript list and work through that rather than through the folder:

set theFolder to (choose folder)

tell application "Finder"
	set theFiles to files of theFolder -- AppleScript list of the files.
	set savedFiles to (make new folder at desktop with properties {name:"Result"})
	repeat with i from 10 to (count theFiles) by 10
		move item i of theFiles to savedFiles
	end repeat
end tell

I’m not sure that xth file of a folder really make sense.
At least, the script below make what yours was supposed to achieve.

The source folder contained :
doc 1.rtf
doc 2.rtf
doc 3.rtf
doc 4.rtf
doc 5.rtf
doc 6.rtf
doc 7.rtf
doc 8.rtf
doc 9.rtf
doc 10.rtf
doc 11.rtf
doc 12.rtf
displayed in this order according to the way Finder sort names with digits.

set i to 0
set theFolder to (choose folder)
set p2d to path to desktop
tell application "Finder"
	set savedFiles to (p2d as text) & "Result:"
	if not (exists folder savedFiles) then
		set savedFiles to (make new folder at desktop with properties {name:"Result"})
	end if
	set savedFiles to savedFiles as alias
	set theFiles to files of theFolder as alias list
	--> {alias "SSD 500:Users:**********:Desktop:source:doc 1.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 10.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 11.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 12.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 2.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 3.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 4.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 5.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 6.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 7.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 8.rtf", alias "SSD 500:Users:**********:Desktop:source:doc 9.rtf"}
(* It means that the app see the list of filenames as:
1 - doc 1.rtf
2 - doc 10.rtf
3 - doc 11.rtf
4 - doc 12.rtf
5 - doc 2.rtf
6 - doc 3.rtf
7 - doc 4.rtf
8 - doc 5.rtf
9 - doc 6.rtf
10 - doc 7.rtf
11 - doc 8.rtf
12 - doc 9.rtf
*)
	repeat
		try
			move item ((i * 3) + 1) of theFiles to savedFiles
		on error
			exit repeat
		end try
		set i to i + 1
	end repeat
end tell

It moved:
i=0 → doc 1.rtf
i=1 → doc 12.rtf
i=2 → doc 4.rtf
i=3 → doc 7.rtf
i=4 → Error
Now, the Result folder contain:
doc 1.rtf
doc 4.rtf
doc 7.rtf
doc 12.rtf

and the source folder contain:
doc 2.rtf
doc 3.rtf
doc 5.rtf
doc 6.rtf
doc 8.rtf
doc 9.rtf
doc 10.rtf
doc 11.rtf

I guess that it would be useful to insert a piece of code sorting the original list so that you may rely upon the ordering of the files.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mardi 2 juillet 2019 14:15:16

Hy Nigel,

thanks a lot. It works great…

Kind Regards

Or perhaps:

set nCount to 10
set theFolder to (choose folder) as text
tell application id "com.apple.finder" -- Finder
	set savedFiles to (make new folder at desktop with properties {name:"Result"})
	set theCount to count of files of folder theFolder
	if theCount > nCount - 1 then
		set theList to {}
		repeat with i from 1 to theCount / nCount
			set end of theList to i * nCount
		end repeat
		move (files of folder theFolder whose index is in theList) to savedFiles
	end if
end tell

Hi Shane.

That’s very slow on my machine. :confused:

A couple of other alternatives are reversed indexing …

set theFolder to (choose folder)

tell application "Finder"
	set savedFiles to (make new folder at desktop with properties {name:"Result"})
	set theCount to (count files of folder theFolder)
	repeat with i from (theCount - theCount mod 10) to 10 by -10
		move item i of theFolder to savedFiles
	end repeat
end tell

… or bulk moving in this manner:

set theFolder to (choose folder)

tell application "Finder"
	set theFiles to files of theFolder -- AppleScript list of the files.
	set savedFiles to (make new folder at desktop with properties {name:"Result"})
end tell

set filesToMove to {}
repeat with i from 10 to (count theFiles) by 10
	set end of filesToMove to item i of theFiles
end repeat

tell application "Finder"
	move filesToMove to savedFiles
end tell

Nigel–a quick question that arose in an earlier thread.

Presumably, the source folder contains a lot of files and, if that is the case, would it not be faster to set theFiles as an alias list? I tested your script with the change and it appeared to work.

Thanks for your help.
I now tried to build a loop to make it a little bit faster.
My idea is, to run the script for 5 times. And with each loop a new folder with the number of the loop is created. I tried this, but no luck…

set theFolder to (choose folder)
repeat with a from 1 to 5
tell application "Finder"
	set theFiles to files of theFolder -- AppleScript list of the files.
	set savedFiles to (make new folder at desktop with properties {name:"Result"& a})
	repeat with i from 10 to (count theFiles) by 10
		move item i of theFiles to savedFiles
	end repeat	
a = a + 1
end tell
end repeat
end tell

I got it:


set theFolder to (choose folder)
repeat with a from 1 to 5
	tell application "Finder"
		set theFiles to files of theFolder -- AppleScript list of the files.
		set savedFiles to (make new folder at desktop with properties {name:"Result" & a})
		repeat with i from 10 to (count theFiles) by 10
			move item i of theFiles to savedFiles
		end repeat
		a = a + 1
	end tell
end repeat

Hi moosmahna.

There’s no need for the a = a + 1 line. a is automatically incremented each time round the outer repeat. In AppleScript, a = a + 1 is just a test to see if a = a + 1, which obviously it doesn’t, so the line generates the unused value false.

Hi peavine.

Possibly. I was simply churning out suggestions for solving the immediate problem, testing with a folder I happened to have on my desktop. I didn’t look very closely at fine-tuning for speed. But I’d encourage anyone who’s interested to experiment.

Nigel–thanks for the response.

I created a test folder containing 5,000 text files and I then ran the script included below ten times, alternating between “as alias list” enabled and then “as alias list” disabled (commented out). The average time of all runs was about 26 seconds and the runs with “as alias list” enabled were consistently one to two seconds slower.

BTW, I used duplicate rather than move to keep the source files intact.

set startTime to (time of (current date))

set theFolder to "Samsung SSD:Test:" as alias

tell application "Finder"
	set theFiles to files of theFolder -- as alias list
	set savedFiles to (make new folder at desktop with properties {name:"Result"})
	repeat with i from 10 to (count theFiles) by 10
		duplicate item i of theFiles to savedFiles
	end repeat
end tell

set executeTime to (time of (current date)) - startTime


Now I go at work. To make script faster you can try 2 things : 1) use System Events instead of Finder 2) maybe exists command line tool for that task, so try do shell script too

OK. But don’t forget that copying takes longer than moving — and by how much depends largely on the sizes of the items being copied. “Moving” only involves changing entries in the disk catalogue and is therefore faster and more nearly the same whatever’s being “moved”.

An ASObjC script would no doubt be fastest of all, but of little help to the OP’s understanding at the moment.

It was meant more for amusement, really. I forgot the smiley :frowning:

Nigel. In retrospect, I didn’t need to move or copy files to see if appending “as alias list” makes things faster or slower. I ran some tests on this and found the latter to be the case. Often an alias list is needed for file processing outside the Finder or for some other reason but otherwise it probably does not need to be used (as was the case with your original script).

Apologies to the OP for getting off-topic.

It’s pretty much a given in my experience with large lists that an alias list will be faster in comparison to Finder’s own references, which are voluminous. If you have very long lists, you can also prepend a file reference with the word “my” and get more than tenfold performance improvement (isolated to list iteration); e.g.:

[format] move item i of my theFiles to savedFiles
[/format]

Edit: There may be a nominal performance penalty in using these tricks in low iteration settings, and 500 is probably not sufficiently long enough.

Marc Anthony. I’ve included my test script below. It takes 10 seconds to run with a test folder containing 20,000 text files and 20 seconds if I uncomment “as alias list”. What you say makes sense but it doesn’t work that way for me–or perhaps I don’t undertand what is happening here.

Thanks for letting me know about adding the word “my” to the script.

myTest()

on myTest()
	set startTime to (time of (current date))
	set theFolder to "Samsung SSD:Test:" as alias
	tell application "Finder" to set theFiles to (files of theFolder) -- as alias list
	set executeTime to (time of (current date)) - startTime
end myTest

BTW, I ran this script from within the Script Editor but I also created separate script applications with and without “as alias list”. The script application without “as alias list” was consistently twice as fast (8 seconds without and 18 seconds with).

FWIW, here’s a pretty fast version using my FileManagerLib script library:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use script "FileManagerLib" version "2.2.1"
use scripting additions

set sourcePath to  (choose folder)
set newFolder to create folder at (path to desktop) use name "Result"
set theContents to objects of sourcePath without include folders
set theContents to sort objects theContents sorted property name property sort type Finder like
repeat with i from 10 to count of theContents by 10
	move object (item i of theContents) to folder newFolder
end repeat