If, for example, what you wanted was to set the Finder selection (in the front Finder window) to N randomly chosen files (so that you could drag them elsewhere, or copy/print, etc) then you could perhaps try something like this:
Select N random files in front Finder window.kmmacros (23.7 KB)
(() => {
'use strict';
const main = () => {
const
kme = Application('Keyboard Maestro Engine'),
finder = Application('Finder'),
folder = getFinderDirectory(),
lrResult = bindLR(
nRandomFilesFromFolderLR(
folder,
parseInt(kme.getvariable('fileCount') || '1', 10),
),
xs => {
try {
// Effect
finder.reveal(map(x => Path(folder + x), xs));
} catch (e) {
// Value
return Left(e.message)
}
// Value
return Right(xs);
}
);
return lrResult.Left || unlines(lrResult.Right);
};
// RANDOM FILE SELECTION ------------------------------
// nRandomFilesFromFolderLR :: FilePath -> n ->
// Either String [FilePath]
const nRandomFilesFromFolderLR = (folderPath, n) => {
const fp = filePath(folderPath) + '/';
return bindLR(
doesDirectoryExist(fp) ? (
Right(
filter(
// Except folders and invisibles
x => !doesDirectoryExist(fp + x) && !('.' === x[0]),
getDirectoryContents(fp)
)
)
) : Left('Folder not found: ' + fp),
xs => n <= xs.length ? (
Right(take(n, knuthShuffle(xs)))
) : Left(
'Less than ' + n.toString() + ' files in \n' + fp
)
);
};
// FINDER ---------------------------------------------
// getFinderDirectory :: IO FilePath
const getFinderDirectory = () =>
Application('Finder')
.insertionLocation()
.url()
.slice(7);
// GENERIC FUNCTIONS ----------------------------------
// https://github.com/RobTrew/prelude-jxa
// Left :: a -> Either a b
const Left = x => ({
type: 'Either',
Left: x
});
// Right :: b -> Either a b
const Right = x => ({
type: 'Either',
Right: x
});
// bindLR (>>=) :: Either a -> (a -> Either b) -> Either b
const bindLR = (m, mf) =>
undefined !== m.Left ? (
m
) : mf(m.Right);
// doesFileExist :: FilePath -> IO Bool
const doesFileExist = strPath => {
const ref = Ref();
return $.NSFileManager.defaultManager
.fileExistsAtPathIsDirectory(
$(strPath)
.stringByStandardizingPath, ref
) && 1 !== ref[0];
};
// doesDirectoryExist :: FilePath -> IO Bool
const doesDirectoryExist = strPath => {
const ref = Ref();
return $.NSFileManager.defaultManager
.fileExistsAtPathIsDirectory(
$(strPath)
.stringByStandardizingPath, ref
) && ref[0];
};
// enumFromTo :: Int -> Int -> [Int]
const enumFromTo = (m, n) =>
m <= n ? iterateUntil(
x => n <= x,
x => 1 + x,
m
) : [];
// filePath :: String -> FilePath
const filePath = s =>
ObjC.unwrap(ObjC.wrap(s)
.stringByStandardizingPath);
// filter :: (a -> Bool) -> [a] -> [a]
const filter = (f, xs) => xs.filter(f);
// getDirectoryContents :: FilePath -> IO [FilePath]
const getDirectoryContents = strPath =>
ObjC.deepUnwrap(
$.NSFileManager.defaultManager
.contentsOfDirectoryAtPathError(
$(strPath)
.stringByStandardizingPath, null
)
);
// iterateUntil :: (a -> Bool) -> (a -> a) -> a -> [a]
const iterateUntil = (p, f, x) => {
const vs = [x];
let h = x;
while (!p(h))(h = f(h), vs.push(h));
return vs;
};
// knuthShuffle :: [a] -> [a]
const knuthShuffle = xs => {
const swapped = (iFrom, iTo, xs) =>
xs.map(
(x, i) => iFrom !== i ? (
iTo !== i ? x : xs[iFrom]
) : xs[iTo]
);
return enumFromTo(0, xs.length - 1)
.reduceRight((a, i) => {
const iRand = randomRInt(0, i);
return i !== iRand ? (
swapped(i, iRand, a)
) : a;
}, xs);
};
// map :: (a -> b) -> [a] -> [b]
const map = (f, xs) =>
(Array.isArray(xs) ? (
xs
) : xs.split('')).map(f);
// randomRInt :: Int -> Int -> Int
const randomRInt = (low, high) =>
low + Math.floor(
(Math.random() * ((high - low) + 1))
);
// showJSON :: a -> String
const showJSON = x => JSON.stringify(x, null, 2);
// take :: Int -> [a] -> [a]
// take :: Int -> String -> String
const take = (n, xs) =>
'GeneratorFunction' !== xs.constructor.constructor.name ? (
xs.slice(0, n)
) : [].concat.apply([], Array.from({
length: n
}, () => {
const x = xs.next();
return x.done ? [] : [x.value];
}));
// unlines :: [String] -> String
const unlines = xs => xs.join('\n');
// MAIN ---
return main();
})();