DeKludge a brute force script

I have a script, which is working, but could be far more elegant. What it’s purpose is, is to sort the words on a line after the “:”. I use this for keywords in documents. For example:

keywords: red yellow green blue

will sort to

keywords: blue green red yellow

The following script works, as long as my prefix is “keywords: “ what I would like to do is make it more elegant to sort only what comes after the “:” regardless of the tag.

// Alpha sort selected lines.
var lnRange = editor.getSelectedLineRange();
var wd = editor.getTextInRange(10,lnRange[1]);
var words = dw.split(’ ‘).sort();
editor.setTextInRange(10,lnRange[1],words.join(’ '));

Still finding my way around javascript, so any suggestions welcome.

Edit: Actually turns out my brute force did not work, it’s messing up lines before the selected line. Back to the drawing board.

You could split on the colon first. Then [0] is static and [1] gets sorted - and then rejoin them.

1 Like

Try this:

var [lStart, lEnd] = editor.getSelectedLineRange();
var line = editor.getTextInRange(lStart, lEnd);
var [heading, rest] = line.split(':');
var words = rest.trim().split(/ +/);
var sorted = words.sort();
var sortedLine = heading + ': ' + sorted.join(' ') + '\n';
editor.setTextInRange(lStart, lEnd, sortedLine);

It uses @martinpacker’s suggestion to split twice: first on the colon and then on spaces.

The trim gets rid of the leading and trailing whitespace (including the trailing linefeed that comes along with getSelectedLineRange) and the regex in the second split treats runs of space characters as single spaces.

The second-to-last line reassembles the line with single separator spaces and puts back the trailing linefeed. If the line of interest happens to be the last line of the file, it may add a trailing linefeed that wasn’t there before.

Usage note: You don’t have to select the line with the keywords. The script works even if you just put the cursor in that line or select any part of it. That’s a nice feature of getSelectedLineRange.

2 Likes

Works perfectly, now to go study and figure out why. Thanks!

Just making sure I understand this. Is the rest.trim().split(/ +/) splitting on one or more spaces, then trimming any extra white space? So the split executes first, then the trim?

Chained method calls execute in the order they read, so the trim runs first, then the split. So, expanded:

// input string
let rest = "  string with  some spaces ";

// trim white space at beginning and end
rest = rest.trim(); // "string with  some spaces"

// split on any number of spaces
let words = words.split(/ +/); // ["string", "with", "some", "spaces"]

Since the split is done with the regex / +/, it will match any number of spaces between words. The trim call before the split is only necessary to avoid getting extra empty entries in the split array if there are spaces at beginning or end.

1 Like

Make that rest.split(...)

After the split(':'), rest will likely have at least one space at the beginning and a newline at the end. It may also have a space or two before the newline. The purpose of trim() is to get rid of all that whitespace. Once it’s gone, then we split the trimmed text into words. As @agiletortoise says, the regular expression argument to the second split is there to account for the possibility of multiple spaces between words. The idea is to make a command that still works when extra spaces appear in the line.

I probably should have done the trim and split on separate lines for clarity, but since you chained split and sort in your original code, I thought chaining would be OK.

Absolutely and I like brevity actually. I just need to learn to read it. I started in FORTRAN like you ended up in COBOL for years, then PERL (the high point). After that my career took a left turn and any programming I did was HyperCard, AppleScript, Shortcuts, Forth (Love Forth), and a few other ods

1 Like

Forth was the language of my mid-20s and had a great influence on how I think about programming. Not sure I could return to it now, but I’ll always hold it in high regard.

2 Likes