Please forgive me for not wording this better - as I get older, I find it harder to be as clear as I'd like to be, and my memory gets foggier and foggier.
Consider this JXA code:
const km = Application("Keyboard Maestro");
const macros = km.macros();
I want to iterate over the macro objects in macros, accessing a few properties for each macro. Let's say I want macro.name() and macro.id().
IIRC, each access of macro.name() and macro.id() is a function call with some amount of overhead, and it seems to me in the back of my mind that there's a way to gather the name() and id() of all the macros in one "call".
Does that make any sense to you? Is there something like this in JXA, or am I mistaken? Thanks.
The trick, I think, is to delay the closing () in km.macros(), and just write km.macros
(The first gives a JS Array, and the second a reference to a Collection)
then, batch-fetching arrays of properties values, and zipping them together:
(() => {
"use strict";
const main = () => {
const km = Application("Keyboard Maestro");
// A reference to a collection, rather than an Array.
// km.macros vs km.macros()
const macros = km.macros;
// Two batch-fetched arrays of property values,
const ids = macros.id();
const names = macros.name();
// zipped together
const pairs = zip(ids)(names);
return JSON.stringify(pairs, null, 2);
};
// --------------------- GENERIC ---------------------
// zip :: [a] -> [b] -> [(a, b)]
const zip = xs =>
// The paired members of xs and ys, up to
// the length of the shorter of the two lists.
ys => Array.from({
length: Math.min(xs.length, ys.length)
}, (_, i) => [xs[i], ys[i]]);
// MAIN ---
return main();
})();
and in practice, a zipWith can be more useful than a zip here:
(() => {
"use strict";
const main = () => {
const km = Application("Keyboard Maestro");
// km.macros vs km.macros()
// Collection reference, vs Array
const macros = km.macros;
// Zipped together with some custom function
// applied over each pair.
return zipWith(
uuid => title => `${uuid}\t${title}`
)(
macros.id()
)(
macros.name()
)
.join("\n");
};
// --------------------- GENERIC ---------------------
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith = f =>
// A list constructed by zipping with a
// custom function, rather than with the
// default tuple constructor.
xs => ys => xs.map(
(x, i) => f(x)(ys[i])
)
.slice(
0, Math.min(xs.length, ys.length)
);
// MAIN ---
return main();
})();
Teachers should always take credit, even though the things they teach are rarely their own ideas.
What I find interesting about your code is that it's not as incomprehensible to me as it once was. I still have to squint my brain to figure it out, but at least I can figure it out now. Mostly. ;p
That's awesome! I had to laugh - when I put the little guy in a quote, I had to escape his right arm. How geeky is that?