Converting lists

Sometimes I realize I’ve been doing something the slow way for a really long time, and it’s kind of embarrassing. A few days ago, I needed to take a column of numbers from a spreadsheet and convert it into a comma-separated list for pasting into Mathematica. I pasted the numbers into BBEdit and did a find/replace to turn

0
12
12
16
12
12
16
12
12
0
0
0
0
0
16
12
12
20
12
12

into

0, 12, 12, 16, 12, 12, 16, 12, 12, 0, 0, 0, 0, 0, 16, 12, 12, 20, 12, 12, 

where I’d searched on linefeeds and replaced them with comma-space pairs. As I was deleting the trailing comma and space so I could ⌘A and ⌘C, it struck me how often I’ve done this. I haven’t converted lists like this for pasting into Mathematica that often, but I sure have done it a lot for making lists in Python, Perl, and even AppleScript. Why haven’t I automated this into a one-step process?

So I did, using Keyboard Maestro. The macro is to be run with a list of newline-separated numbers on the clipboard (we’ll deal with a list of strings later), and when the macro is invoked, the output will be a list of comma-separated numbers. There will be no trailing newline because I’ll be pasting the output between brackets (for Python or Perl) or braces (for AppleScript or Mathematica), and a trailing newline would have to be deleted.

You might be wondering whether a column of numbers copied from a spreadsheet fits the criterion of “newline-separated.” One of the great things about the Mac clipboard is that it can store its contents in multiple formats and paste the format that’s most appropriate. If you’re pasting into a spreadsheet, it will keep the cell structure, including any formulas; if you’re pasting into a plain text document, it will paste the visible text as a series of lines. As you’ll see in a minute, we’re going to use the pbpaste command, which

removes the data from the pasteboard and writes it to the standard output. It normally looks first for plain text data in the pasteboard and writes that to the standard output; if no plain text data is in the pasteboard it looks for Encapsulated PostScript; if no EPS is present it looks for Rich Text. If none of those types is present in the pasteboard, pbpaste produces no output.

Here’s what the ridiculously short macro looks like,

KM Paste clipboard lines as comma-separated list of numbers

and you can download it if you can’t be bothered to type out that little shell script.1 I have it stored in my Global Macro Group, so the pasting works in any app.

The shell script is

pbpaste | perl -nle 's/,//g; push @a, $_; END{print join(", ", @a)}' 

As we saw above, pbpaste outputs the list as several lines of text, which gets piped to perl. The Perl one-liner reads in each line of input (that’s the -n option), deletes its trailing newline (-l), deletes any commas that might be in it (s/,//g),2 and puts the resulting item at the end of a list (push). After all the lines have been read (END), the list is printed out with comma-space pairs between the items (join). So if I copy this column,

1,174
  918
  782
  844
1,213

and invoke the macro with ⌃⌥⌘V, this will be pasted into the active app:

1174, 918, 782, 844, 1213

If you’re familiar with Perl’s -l option, you might be wondering why there’s no newline at the end of this output. Well, Perl does add a trailing newline, but Keyboard Maestro has an option in the gear menu that trims it before pasting.

KM gear menu

To paste the output of the shell script, Keyboard Maestro first puts it on the clipboard. But I don’t want that; I want the clipboard the way it was before I ran the macro. Running this macro is like pasting, and pasting shouldn’t change the clipboard. This is where the second action comes in.

Keyboard Maestro keeps a clipboard history and has commands for manipulating that history. By deleting past clipboard item 0, I’ve deleted the item that Keyboard Maestro itself just put onto the clipboard, bringing it back to the state it was before I invoked the macro.

Sometimes the list I want to convert is a list of strings instead of just numbers. To tell the programming language that the items are strings, I have to enclose them in quotation marks. I’ll use double quotation marks, because all of the languages I use understand them, and I’ll escape any double quotation marks that happen to appear within the strings. Here’s the resulting macro,

KM Paste clipboard lines as comma-separated list of quoted strings

which you can download here.

The shell script is only slightly more complicated:

pbpaste | perl -nle 's/"/\\"/g; push @a, $_; END {print q("), join(q(", "), @a), q(")}'

Here, we don’t delete commas, but we do escape double quotation marks. And the END section is more complicated because we need to enclose every item in quotation marks. I’m using Perl’s q operator to make the quoting of quotation marks easier to read.

If I put

Here are
some lines
with "really good"
text

on the clipboard and run this macro with ⌃⌥⇧⌘V, the output will be

"Here are", "some lines", "with \"really good\"", "text"

A few last notes:


  1. If your browser opens the link to the macro and displays the XML contents, control-click (right-click) it to bring up a menu that allows you to download the file. 

  2. None of the languages I use can interpret commas as thousands separators. They’ll see them as item separators, so they need to go.