Reverse Word Order of Lines in the Clipboard

can't figure this one out. How can I reverse the words order?

  1. Highlight selected text "First_Name Last_Name"

  2. Copy to clipboard

  3. Change to "Last_Name, First_Name"

  4. Paste clipboard

Regex to the rescue:

Reverse Words in Clipboard.kmmacros (1.7 KB)

5 Likes

Thank you. This is magical. Learning how to use Regex is on my ToDo list.

2 Likes

The solution provided above by @gglick should work fine, as long as your source data is exactly as you stated in your OP.

Here is another solution.

Regex: (\w+)\h+(\w+)(.*)
Regex Info: regex101: build, test, and debug regex


==UPDATED:== 2017-10-14 18:07 CT

Chris (@ccstone) made an excellent point in his post below. So I'm updating this post.

The names could also be hyphenated. So the RegEx pattern in my macro should be:

([\w\.\-]+)\h+([\w\.\-]+)(.*)

See regex101: build, test, and debug regex

Until I have a chance to update my below macro, please use this RegEx.


This RegEx will meet the specific requirements of the OP, plus it will handle more general cases where:

  • More than one whitespace character is between words
  • Whitespace characters other than SPACE and TAB are used
  • There is additional text on source line AFTER the first two words

Example Output

OR, Alternately:

(by enabling the Second Replace Action

OR, using the OP's source data:

MACRO:   Swap First Two Words in String [Example]


VER: 1.0    2017-10-13 ~~~

DOWNLOAD:

Swap First Two Words in String [Example].kmmacros (7.9 KB)
Note: This Macro was uploaded in a DISABLED state. You must enable before it can be triggered.


3 Likes

For what it's worth, the reason I used [[:blank:]] is because I remembered this statement from @peternlewis in the horizontal whitespace discussion:

If it turns out that \h is actually the best general expression for matching white space characters without including line breaks, I'll be happy to use that going forward. It's certainly a lot easier to type! :wink:

I believe that is the case for KM.

As our discussion indicates in the topic RegEx for Horizontal Whitespace (\s \h \t blank etc) there is considerable confusion about this.

My research today seems to show that [[:blank:]] includes ONLY SPACE and TAB characters. IAC, there is no doubt about what \h includes, so, IMO, it is the best choice in KM RegEx Actions to use for horizontal whitespace.

If you find a reference that indicates differently, please post in RegEx for Horizontal Whitespace (\s \h \t blank etc)

1 Like

Hey slyfox,

Let’s try doing that with a few lines of AppleScript:

# Simulate getting your text onto the clipboard.
set the clipboard to "First_Name Last_Name"

# The word reversal code:
set wordList to the clipboard
set AppleScript's text item delimiters to ", "
set newList to reverse of (words of wordList) as text

# Set the clipboard to the new text:
set the clipboard to newList

Now let’s show off and abbreviate that middle section just a bit:

# The word reversal code in only 2 lines:
set AppleScript's text item delimiters to  ", "
set newList to reverse of (words of (get the clipboard)) as text

Note — AppleScript can be finicky in deciding what a word is, so I generally prefer to use more precise methods — but for many jobs words of works well.

Lastly I’ll do it using the Satimage.osax (this method does not assume there will only be two words):

The Satimage.osax MUST be installed on your system for the following scripts to work!

set wordList to "First_Name Last_Name"
set wordList to find text "[^[:blank:]]+" in wordList with regexp, all occurrences and string result
set AppleScript's text item delimiters to ", "
set wordList to (reverse of wordList) as text

Knowing for certain that I’m dealing with only two words I’d be more likely to do this:

set wordList to "First_Name Last_Name"
set wordList to change "^[[:blank:]]*([^[:blank:]]+)[[:blank:]]+([^[:blank:]]+)[[:blank:]]*" into "\\2, \\1" in wordList with regexp without case sensitive

You can see that I’m allowing for the possibility of leading and/or trailing whitespace.

In an application like this I would not generally use the \w metacharacter, because a first name might be listed as an initial with punctuation.

-Chris

4 Likes

Excellent point, Chris. The names could also be hyphenated. So the RegEx pattern in my macro should be:

([\w\.\-]+)\h+([\w\.\-]+)(.*)

See regex101: build, test, and debug regex

A lazy route through Execute Script actions (JavaScript or AppleScript) might be to paste 3 generic functions (intercalate, words, reverse), and compose a solution from them.

Often enough …

e.g. (Donald Duck -> Duck, Donald) in JavaScript and then AppleScript:

(() => {
    'use strict';

    // GENERIC FUNCTIONS -----------------------------------------------------

        // intercalate :: String -> [String] -> String
        const intercalate = (s, xs) => xs.join(s);

        // reverse :: [a] -> [a]
        const reverse = xs =>
            typeof xs === 'string' ? (
                xs.split('')
                .reverse()
                .join('')
            ) : xs.slice(0)
            .reverse();

        // words :: String -> [String]
        const words = s => s.split(/\s+/);

        // MAIN ------------------------------------------------------------------
        const
            a = Application.currentApplication(),
            sa = (a.includeStandardAdditions = true, a),
            strClip = intercalate(
                ', ',
                reverse(words(sa.theClipboard()))
            );

        return (
            sa.setTheClipboardTo(strClip),
            strClip
        );
    })();

-- GENERIC FUNCTIONS ---------------------------------------------------------

-- intercalate :: String -> [String] -> String
on intercalate(s, xs)
    set {dlm, my text item delimiters} to {my text item delimiters, s}
    set str to xs as text
    set my text item delimiters to dlm
    return str
end intercalate

-- reverse :: [a] -> [a]
on |reverse|(xs)
    if class of xs is text then
        (reverse of characters of xs) as text
    else
        reverse of xs
    end if
end |reverse|

-- words :: String -> [String]
on |words|(s)
    words of s
end |words|

-- TEST ----------------------------------------------------------------------
on run
    set strClip to intercalate(", ", |reverse|(|words|(the clipboard)))
    set the clipboard to strClip
    strClip
end run

The main downside with \h is that it requires 10.11+.

That and it brings back memories of \R also requiring 10.11+ which is what removed all the capital Rs from Keyboard Maestro 8.0 macros in 10.10. It's a painful memory, not something I'll likely forget any time soon!

1 Like

Well, 10.10 is long ago and I don't see any reason to support legacy systems that nobody (unless required by the client) is using. When it comes to macros for personal use it isn't an issue anyway.

When writing programs for some server you have to take in account the prerequisites anyway. But this doesn't concern KM (I think).

Noted. I always provide a "Requires" section in my macros that all include a requirement for 10.11+. If I wanted to use a RegEx horizontal whitespace that was more compatible, I would use [^\S\r\n\f]

Keyboard Maestro 8 still supports 10.10, although only a few percent of users run version 8 on 10.10.

Hi, this looks extremely useful. Unfortunately, I can’t figure out how to make it work. I downloaded and installed it, enabled it, and added a trigger. When I copy a name (e.g., John Smith) and type my trigger I only get the following:

The Text is:

Source String:
First_Name Last_Name

Result String:
Last_Name, First_Name

I apologize if this is really obvious! Thanks.

It sounds like you left this action unchanged:

If you add a Copy action before it and set this variable to use %SystemClipboard%:

and change the "Display Text "Source String:…" in Window" action so that it instead just pastes %Local__ResultStr% at the end:


then it should work as expected. Feel free to post again if you still can't get it to work or have any further questions.

Here’s what I did but it is still not working for me. I’m only using the default System Clipboard, but I was thinking that it wasn’t working because possibly a different clipboard was in use. I believe I’ve got the changes you indicated. Could you take a look and let me know?

Everything looks right in your screenshot, so I'm not sure why it's still not working for you. In case it helps, here's a version with the same changes that I've verified works as expected:

Swap First Two Words in String [Example].kmmacros (7.0 KB)

I did take the liberty of disabling the first Search And Replace action to ensure it was just switching the first two words and not touching the rest of the string, but it still worked fine for me either way.

Actually, now that I think of it, there may be one thing going on that might explain why it's still not working for you: are you copying the text and then running the macro, or are you selecting the text and running it? If it's the former, then you can disable the initial Copy action; otherwise, you should be able to leave it as is and still have it work.

That’s it! I’m copying the text and then running the macro, so I disabled the initial Copy action. Thanks so much for your help!

1 Like

That works great for a list I had first name last name - how can I add alphabetizing by last name to the end of this?

One of the simplest ways to do this is to use an Execute Shell Script action to run the Unix sort command on the results variable:

Execute a Shell Script.kmactions (761 Bytes)

On this sample list of names:

John Smith
Dave Jones
Alicia Feldman
Sharon Davis

it makes the macro produce this output:

Davis, Sharon
Feldman, Alicia
Jones, Dave
Smith, John

Just add that action right after the Search and Replace action you use. It should look something like this: