Sort lines not working as I intend them to be sorted

I have a list of paths with a number in the beginning (inside parenthesis), and after I use the Sort filter, I get this:
image

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

image

From the topic "Numerically sort clipboard contents":

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.

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
    );
})();

Yes, sorry, my test macro was buggy (I hadn't actually used the shell script action to save the contents to the clipboard).

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 sorts nifty "version sort" option, sort -V:

1 Like

Drat, you are always giving better answers than me, and mine was pretty good this time.

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)