Continuing the discussion from Using RegEx to sort text in a variable?:
The above topic has generated much discussion, and several excellent solutions (macros/scripts) to properly sort a list of file names, particularly if you want to sort only on the file name without the extension.
Solutions Offered in Original Topic
(click on link to view post where macro is offered)
-
Solution selected by OP:
[example] Sort Lines (Perl).kmmacros using Perl β by @Tom -
Sort Lines [test].kmmacros using
sort
cmd β by @mrpasini - Sort Lines With Pattern.kmmacros using Perl β 2nd solution by @mrpasini
- [example] Sort Lines With Pattern.kmmacros using Perl β 2nd solution by @Tom
- Sorted filenames.kmmacros using JXA ES6
- Sorted filenames by splits.kmmacros using JXA ES6 β 2nd solution
@Tom pointed out in his 2nd solution the need to handle file names that contain periods, which his solution does.
I have to say that @Tomβs 2nd solution, using Perl, is very powerful while also being compact. It used only 11 lines of code!
His code is also very readable and maintainable.
I was hoping that a JXA (JavaScript) solution would also be simple and compact, but unfortunately both JXA solutions are for me hard to read.
So, just as a matter of interest for myself, and anyone else that might like a JXA solution, I have developed a JXA script that is fairly compact (only 26 lines of code), and, IMO, very readable (others may disagree).
<img src="/uploads/default/original/2X/9/9fa034801953dfe7d1745afcb7087c7530a06e23.gif" width="70" height="17"> 2017-09-18 19:28 CT
### My JXA Script/Macro to Sort List of File Names
Revised the script so that it does a 2nd Level sort on file ext
* Handles Periods in the file name
* Sorts FIRST on the file name, THEN on the extension
* Makes use of a RegEx pattern in the JavaScript `split()` function to split the full file name into an array with [FileName, Ext], for each row, but also handles files without an extension.
* Uses @Tom's list of files plus these:
Water 01.jpg
clip 03.wav
File With No Ext
This script uses ES5 JavaScript, compatible with macOS 10.11.6+ (El Capitan+). No Babel required. :wink:
**As always, please feel free to ask any questions, and make any suggestion to improve my macro/script.**
---
### Example Output
Using Revised List of Files
<img src="/uploads/default/original/2X/f/f251989403dd5e056e464d1a6d4e74c28d264408.png" width="359" height="348">
Note how, now, "Water 01.jpg" sorts before "Water 01.wav".
---
<img src="/uploads/default/original/2X/9/9fa034801953dfe7d1745afcb7087c7530a06e23.gif" width="70" height="17"> 2017-09-18 19:28 CT
###MACRO: @Sort List of FileNames using JXA [Example]
~~~ VER: 2.0 2017-09-18 ~~~
####DOWNLOAD:
<a class="attachment" href="/uploads/default/original/2X/1/10a5cfb22d570a95699794b20a654091805ed65a.kmmacros">@Sort List of FileNames using JXA [Example].kmmacros</a> (7.2 KB)
**Note: This Macro was uploaded in a DISABLED state. You must enable before it can be triggered.**
---
<img src="/uploads/default/original/2X/e/ef59a9b3fd53f6596ce422906f7dad9b6e0b4121.png" width="565" height="1181">
JXA Script (ES5, El Capitan+ Compatible)
'use strict';
(function run() { // this will auto-run when script is executed
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PURPOSE: Sort List of FileNames on FileName Then Extension
VER: 2.0 2017-09-18
AUTHOR: @JMichaelTX
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
var fileNamesStr = Application("Keyboard Maestro Engine").getvariable('SFN__FileList');
var scriptResults = "TBD"
//--- Convert String List to JS Array ---
var fileList = fileNamesStr.split(/[\r\n]/)
//--- Convert Each Row (FileName) into Array of (FileName), (Ext) ---
// so that we can sort based ONLY on root file name
//--- Make use of RegEx Pattern in the JS split() function to split on last period ---
var table = fileList.map(function(lineStr) {
return lineStr.split(/\.(?=[^.]*$)/);
})
//--- Sort File List on Root FileName [0], Then on File Extension [1] ---
var sortedTable = table.sort( sortMultiCol([0,1]) );
//--- Re-Join Multil-Column Array into String for Return ---
scriptResults = sortedTable.map(function(e) { return e.join('.');}).join('\n');
//console.log(scriptresCompares);
return scriptResults;
//~~~~~~~~~~~~~~~~ END OF MAIN SCRIPT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function sortMultiCol(pColsToSortArr) {
/* Ver 2.0 2017-09-18
---------------------------------------------------------------------------------
PURPOSE: Sort Array on One or More Columns, In Specified Order
Numbers will be sorted as numbers.
Numbers in quotes will be sorted as strings.
USAGE: someArray.sort( sortMultiCol([3,1] ); // [3,1] is just an example.
PARAMETERS:
β’ pColsToSortArr | Array of Integers | Column Numbers to Sort
RETURNS: Sorted Array
AUTHOR: JMichaelTX
REF:
1. robbmj, 2015-03-10, https://stackoverflow.com/a/28969807/915019
TODO: Add option for sort direction.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
*/
var resCompare, iCol;
return function(a, b) {
//--- Loop Through All Requested Cols Until Comparision β 0 ---
for (var idx = 0; idx < pColsToSortArr.length; idx++) {
iCol = pColsToSortArr[idx];
if (typeof a[iCol] === 'number') {
//--- Compare a Numbers ---
resCompare = a[iCol] - b[iCol];
//--- Compare as Strings (localeCompare() has other options ---
} else {resCompare = a[iCol].localeCompare(b[iCol]);} // case insensitive
//--- Keep Looping Until Comparision β 0, or All Cols have been Compared ---
if (resCompare !== 0) { break; } // for
} // END for
return resCompare;
};
} //~~~~~~~~~~~~~~~ END OF function sortMultiCol ~~~~~~~~~~~~~~~~~~~~~~~~~~~
})(); // auto-run function when script is executed.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ END OF FILE ~~~~~~~~~~~~~~~~~~~~~~~~~~