Emacs: convert items on separate lines to a comma-separated list
Asked Answered
H

5

7

I often paste items separated by newlines or line feeds into an Emacs buffer resulting in each item residing on a different line like this:

one
two
three
four

Very often I actually want a list of comma-separated values like this:

"one", "two", "three", "four"

It would be great to be able to do a one-touch conversion from lines to list. I imagine I can convert this using a regex, but it seems like the kind of commonly used operation that might already have a built-in Emacs function. Can anybody suggest one?

Hooper answered 29/7, 2013 at 10:55 Comment(0)
S
7

M-q would replace linebreaks with spaces (in reasonably short lists of short words) but won't add quotes and commas. Or, maybe M-^ many times, until you have them all on the same line. Other than that - nothing built in comes to mind.

Obviously, a keyboard macro is a good candidate for this.

But a faster way, that doesn't create many undo steps would be something along these lines:

(defun lines-to-cslist (start end &optional arg)
  (interactive "r\nP")
  (let ((insertion
         (mapconcat 
          (lambda (x) (format "\"%s\"" x))
          (split-string (buffer-substring start end)) ", ")))
    (delete-region start end)
    (insert insertion)
    (when arg (forward-char (length insertion)))))
Satyriasis answered 29/7, 2013 at 11:49 Comment(3)
The cursor should be positioned just after the last line in the list for this function to work as expected, but that is probably the natural place for the cursor to be if you've just pasted something into the buffer. This works for me - thanks. Edit: And this, incidentally, if why I like Emacs!Hooper
@Hooper I've changed it to use universal argument as an instruction to jump to the place after insertion, so if you call it M-x lines-to-cslist, the point will move to the end of the inserted string.Satyriasis
This has been very helpful to me. There's a small flaw when I use it as the default split values for split-string includes spaces and other common separators. I worked around this by adding "\n" to the call to split-string but this could also be solved by customising split-string-default-separatorsHaag
B
6

Edit: I do see you're looking for a function... but since the only answer is simply to write your own (i.e. no built-in one exists), I figured I'd chime in with what the regex would be, since others may stumble on this and appreciate an alternative to writing the function and putting it in .emacs.


This is two steps, but only because you wanted your text quoted:

As pasted in Emacs *scratch* buffer (added five six to show it works with multiple words per line, if that's of interest):

one
two
three
four
five six

First, replace individual word with "word":

M-x replace-regexp RET \(.*\) RET "\1" RET produces:

"one"
"two"
"three"
"four"
"five six"

Now, replace each carriage return (in Emacs, C-q C-j) with a ,:

M-x replace-regexp RET C-q C-j RET , RET produces:

"one", "two", "three", "four", "five six"
Belk answered 13/4, 2014 at 0:10 Comment(0)
H
5

I wrote a solution for this at work today. Below are functions to convert from lines to csv, and from csv to lines, with a user-specify-able separator. This function operates on the currently-highlighted region.

(defun lines-to-csv (separator)
  "Converts the current region lines to a single line, CSV value, separated by the provided separator string."
  (interactive "sEnter separator character: ")
  (setq current-region-string (buffer-substring-no-properties (region-beginning) (region-end)))
  (insert
   (mapconcat 'identity
              (split-string current-region-string "\n")
              separator)))

(defun csv-to-lines (separator)
  "Converts the current region line, as a csv string, to a set of independent lines, splitting the string based on the provided separator."
  (interactive "sEnter separator character: ")
  (setq current-region-string (buffer-substring-no-properties (region-beginning) (region-end)))
  (insert
   (mapconcat 'identity
              (split-string current-region-string separator)
              "\n")))

To use this, highlight the region you want to edit, then do M-x and specify the separator to use.

Hyacinthia answered 17/10, 2014 at 20:56 Comment(0)
N
2

I usually accomplish these kinds of tasks using macros. M-X kmacro-start-macro and M-x kmacro-end-or-call-macro, which you can then do repeatedly then.

Nicaea answered 18/10, 2014 at 7:3 Comment(0)
D
0

I have these definitions in my ~/.emacs:

(defun join-line-after ()
  "Join this line to next and fix up whitespace at join."
  (interactive)
  (end-of-line)
  (forward-char)
  (join-line))

(global-set-key "\C-j" 'join-line-after)

So, to turn a list of lines into one line, I place the cursor before the first element and keep hitting C-j. If commas need to be added or removed, I'll usually do that with a regexp replacement, before or after, depending on the context.

Disgruntle answered 22/2 at 18:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.