I have a list of paths with a number in the beginning (inside parenthesis), and after I use the Sort filter, I get this:
I tried some suggestions from ChatGPT and Claude, but it doesn't work. They suggest adding zeros, which I tried using different approaches, but it doesn't seem to work.
Any tips?
I just used a (probably) convoluted approach, but it's working
kevinb
January 25, 2025, 1:25pm
3
I saw another thread sharing the same solution, but that only works if the numbers are the first characters, like this:
48 jdjaos
1 jskdjf
399 djosd
28 jksjdkf
Once you have something like this:
(48) jdjaos
(1) jskdjf
(399) djosd
(28) jksjdkf
it doesn't work. That's why I had to add those zeros.
Airy
January 25, 2025, 5:30pm
5
This works for me:
sort -n -t "(" -k 2 num.txt
which gives:
(0) hfsjk
(1) jskdjf
(28) jksjdkf
(48) jdjaos
(301) fhsjkfs
(399) djosd
FWIW, you could try using Intl.Collator
JS object.
Intl.Collator - JavaScript | MDN
The Intl.Collator object enables language-sensitive string comparison.
Perhaps something like this could work:
Download Macro(s): Sorted lines in ascending order.kmmacros (3.2 KB)
Macro-Image
Macro-Notes
Macros are always disabled when imported into the Keyboard Maestro Editor.
The user must ensure the macro is enabled.
The user must also ensure the macro's parent macro-group is enabled.
System Information
macOS 14.7.2
Keyboard Maestro v11.0.3
(() => {
"use strict";
// jxaContext :: IO ()
const jxaContext = txt => {
// main :: IO ()
const main = () => {
return naturalSort(
x => x
)(
lines(txt).map(
x => x.trim()
)
)
};
// ------- JS Basics -------
// naturalSort :: (a -> String) -> [a] -> [a]
const naturalSort = f => xs => {
const collator = new Intl.Collator(
undefined, {
numeric: true,
sensitivity: 'base'
}
)
return xs.sort((a, b) => collator.compare(f(a), f(b)))
};
// lines :: String -> [String]
const lines = s =>
// A list of strings derived from a single string
// which is delimited by \n or by \r\n or \r.
0 < s.length ?
s.split(/\r\n|\n|\r/u) :
[];
// MAIN --
return main();
};
return JSON.stringify(
jxaContext(
kmvar.localText
), null, 2
);
})();
kevinb
January 25, 2025, 5:49pm
8
Yes, sorry, my test macro was buggy (I hadn't actually used the shell script action to save the contents to the clipboard).
Nige_S
January 25, 2025, 7:16pm
9
Which makes it work as an alphabetical sort (0
comes before 1
). You could do it the other way round, treating them as numbers, by deleting the leading (
s then using the sort -n
shell action.
If you want to do it without changing the text you can use the shell and sort
s nifty "version sort" option, sort -V
:
1 Like
Airy
January 25, 2025, 7:35pm
10
Drat, you are always giving better answers than me, and mine was pretty good this time.
Nige_S
January 25, 2025, 7:43pm
11
Yours is a better general solution, allowing the choice of delimiter and column to sort on. Mine's (ab)using the format of @alltiagocom 's data -- specifically that every line has the same prefix, (
, before the number to be sorted on. It wouldn't work on
a(48) zdjaos
b(1) jskdjf
c(399) djosd
d(28) jksjdkf
...for example, whereas yours will.
If we want to allow for infixes as well as prefixes,
and also handle lines which:
don't have numbers in their first bracketed infix, xyz(??)
, or
don't have a bracketed infix at all xyzNoNumericInfix
i.e. for numeric sorting of input like:
a(48) zdjaos
(1) jskdjf
c(??)
d(399) djosd
xyzNoNumericInfix
f(28) jksjdkf
then in a JS action we can define, in terms of splits, how we obtain sortable numbers (pushing eccentrics right to the end):
const bracketedNum = s => {
const d = (s.split( "(" )[1] || "").split( ")" )[0];
return 0 === d.length || isNaN(d) ? Infinity : Number(d);
};
and then compare the (-1, 0, 1)
(less, same, more
) ordering of any given pair of lines accordingly:
return kmvar.local_Source
.split("\n")
.sort((lineA, lineB) => {
const [m, n] = [lineA, lineB].map(bracketedNum);
return m < n
? -1
: m > n
? 1
: 0
})
.join("\n")
Lines sorted numerically by bracketed numeric infixes.kmmacros (3.7 KB)