# Getting Timestamp of Most Recently Modified Macro

I found a solution, enlisting my son (a developer in Amsterdam) to modify the solution in Post #2 to give me all macros, sorted by descending modification date, with human readable dates instead of the epoch format. If you're interested, here it is:

``````(() => {
"use strict";

const prettyDate = (t) => new Intl.DateTimeFormat('en-US', { dateStyle: 'full', timeStyle: 'short', timeZone: 'America/Denver'}).format(new Date(Number(t)));
const messy = (line) => { const p=line.split(' '); return prettyDate(p[0]) + " " + p.splice(1).join(' '); }
const main = () => {
const
km = Application("Keyboard Maestro"),
macros = km.macros;

return sortBy(
flip(compare)
)(
zipWith(
t => k => `\${new Date(t).getTime()} -> \${k}`
)(
macros.modificationDate()
)(
macros.name()
)
)
.map(messy)
.join("\n");
};

// ------------------ GENERICS -------------------
// https://github.com/RobTrew/prelude-jxa
// ----------------- JS PRELUDE ------------------

// compare :: a -> a -> Ordering
const compare = a =>
b => a < b ? -1 : (a > b ? 1 : 0);

// flip :: (a -> b -> c) -> b -> a -> c
const flip = op =>
// The binary function op with
// its arguments reversed.
1 !== op.length
? (a, b) => op(b, a)
: (a => b => op(b)(a));

// sortBy :: (a -> a -> Ordering) -> [a] -> [a]
const sortBy = f =>
// A copy of xs sorted by the comparator function f.
xs => xs.slice()
.sort((a, b) => f(a)(b));

// zipWith :: (a -> a -> b) -> [a] -> [b]
const zipWith = f => {
// A list with the length of the shorter of
// xs and ys, defined by zipping with a
// custom function, rather than with the
// default tuple constructor.
const go = xs =>
ys => 0 < xs.length
? 0 < ys.length
? [f(xs[0])(ys[0])].concat(
go(xs.slice(1))(ys.slice(1))
)
: []
: [];

return go;
};

// MAIN --
return main();
})();

``````

I've managed to tweak the JavaScript to create a zipWith4 that works if I want to add, say, the size of the macro:

``````(() => {
"use strict";

const main = () => {
const
km = Application("Keyboard Maestro"),
macros = km.macros;

return sortBy(
flip(compare)
)(
zipWith4(
t => k => u => v =>
`\${new Date(t).getTime()} -> \${u} -> \${k} -> \${v}`
)(
macros.modificationDate()
)(
macros.name()
)(
macros.id()
)(
macros.size()
)
)
.join("\n");
};

// ------------------ GENERICS -------------------
// https://github.com/RobTrew/prelude-jxa
// ----------------- JS PRELUDE ------------------

// compare :: a -> a -> Ordering
const compare = a =>
b => a < b ? -1 : (a > b ? 1 : 0);

// flip :: (a -> b -> c) -> b -> a -> c
const flip = op =>
// The binary function op with
// its arguments reversed.
1 !== op.length
? (a, b) => op(b, a)
: (a => b => op(b)(a));

// sortBy :: (a -> a -> Ordering) -> [a] -> [a]
const sortBy = f =>
// A copy of xs sorted by the comparator function f.
xs => xs.slice()
.sort((a, b) => f(a)(b));

// zipWith4 :: (a -> b -> c -> d -> e) ->
// [a] -> [b] -> [c] -> [d] -> [e]
const zipWith4 = f =>
xs => ys => zs => zzs => Array.from({
length: Math.min(
...[xs, ys, zs, zzs].map(x => x.length)
)
}, (_, i) => f(xs[i])(ys[i])(zs[i])(zzs[i]));

// MAIN --
return main();
})();
``````

That works perfectly. But what I'd really like is the UUID of the group containing the macro, not the size. I tried this for the fourth parameter:

`macros.macrogroup().id()`

But that failed ... I assume there's some way to get this, but it probably involves a mapping between the two data sets that I just don't know about ... all help appreciated!

-rob.

Wild guess -- is JavaScript case sensitive? KM's dictionary shows it as `macroGroup`, with a capital "G".

I tried both ways, no joy—it doesn't seem to be, as the JavaScript dictionary in Script Editor shows it's the Macros object, but macros works in the script.

I've also tried `macros.macrgroup().id()` without luck. Clearly my lack of JS knowledge is the stumbling point here :).

-rob.

`macrogroup()` contains two problems:

1. Should be `macroGroups` for the app or `macroGroup` for a single macro (consult the SDEF in Script Editor, or Script Debugger), and
2. calling that method by adding `()` would lose the reference to a `.MacroGroups` collection which does have has an `id()` method, trading it in for just a JS Array (of Macro Group values), which has no such method.

Whereas:

``````Application("Keyboard Maestro").macroGroups.macros.id()
``````

will evaluate to a list of lists (one list of macro UUIDs for each macroGroup)

and if you want redundancy - one group UUID value for every macro:

``````Application("Keyboard Maestro").macros.macroGroup.id()
``````

See

OS X 10.10 Release Notes - JXA

1 Like

OMG that's exactly what I needed! Thank you so much—with some work now on my part, this could speed my MacroBackerUpper macro by an order of magnitude or two or ten!

-rob.

1 Like

PS there's already a pre-baked `zipWith4` in my library at:

(tho these days for arbitrary scaling of zipping beyond `zipWith3` I tend to use compositions of `zipWith(x => x)`, which save having to write new functions)

1 Like