Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to distribute strings in Emacs or Vim

Tags:

vim

emacs

In Emacs or Vim, what's a smooth way to join strings as in this example:

Transform from:
    (alpha, beta, gamma) blah (123, 456, 789)
To:
    (alpha=123, beta=456, gamma=789)

It would need to scale to:

  • many lines of these
  • many elements in the parentheses

I have recently found myself needing this kind of transformation often.

I use Evil in Emacs which is why a Vim answer would likely also help.

UPDATE:

The solutions were not as general as I had hoped. For example, I'd like the solution to also work when I have a list of strings and wish to distribute them into a large XML document. eg:

<item foo="" bar="barval1"/>
<item foo="" bar="barval2"/>
<item foo="" bar="barval3"/>
<item foo="" bar="barval4"/>

fooval1
fooval2
fooval3
fooval4

I formulated a solution and have added it as an answer.

like image 960
TheDude Avatar asked Dec 19 '12 23:12

TheDude


3 Answers

%s/(\(\S\{-}\), \(\S\{-}\), \(\S\{-}\)).\{-}(\(\S\{-}\), \(\S\{-}\), \(\S\{-}\))/(\1=\4, \2=\5, \3=\6)

%s: global search and replace

\(\S{-}\),: non greedy search for non-whitespace characters up to the next comma, enclosed by "(" for backreferencing

\1=\4 : prints out the first match, an "=" sign, then the fourth match

like image 111
ash Avatar answered Nov 11 '22 22:11

ash


for such text transformation, I would go with awk:

this one-liner may help:

awk -F'\\(|\\)' '{split($2,t,",");split($4,v,",");printf "( "; for(x in t)s=s""sprintf("%s=%s, ", t[x],v[x]);sub(", $","",s);printf s")\n";s=""}' file

little test:

kent$  cat test
(alpha, beta, gamma) blah (123, 456, 789)
(a, b, c) foo (1, 2, 3)
(x, y, z, m, n) bar (100, 200, 300, 400, 500)

kent$  awk -F'\\(|\\)' '{split($2,t,",");split($4,v,",");printf "( "; for(x in t)s=s""sprintf("%s=%s, ", t[x],v[x]);sub(", $","",s);printf s")\n";s=""}' test

( alpha=123,  beta= 456,  gamma= 789)
( a=1,  b= 2,  c= 3)
(  m= 400,  n= 500, x=100,  y= 200,  z= 300)
like image 28
Kent Avatar answered Nov 11 '22 21:11

Kent


Emacs Lisp version of Prince Goulash answer

(require 'cl)

(defun split-and-trim (str separator)
  (let ((strs (split-string str separator)))
    (mapcar (lambda (s)
              (replace-regexp-in-string "^\\s-+" "" s))
            (mapcar (lambda (s)
                      (replace-regexp-in-string "\\s-$" "" s)) strs))))

(defun my/merge-list (beg end)
  (interactive "r")
  (goto-char beg)
  (let ((endmark (set-mark end))
        (regexp "(\\([^)]+\\))[^(]+(\\([^)]+\\))"))
    (while (re-search-forward regexp end t)
      (let ((replace-start (match-beginning 0))
            (replace-end   (match-end 0))
            (keys-str (match-string-no-properties 1))
            (values-str (match-string-no-properties 2)))
        (let* ((keys (split-and-trim keys-str ","))
               (values (split-and-trim values-str ",")))
          (while (> (length keys) (length values))
            (setq values (append values '(""))))
          (let* ((pairs (mapcar* (lambda (k v)
                                   (format "%s=%s" k v)) keys values))
                 (transformed (format "(%s)" (mapconcat #'identity pairs ", "))))
            (goto-char replace-start)
            (delete-region replace-start replace-end)
            (insert transformed)))))
    (goto-char (marker-position endmark))))

For example, you select region as following

(alpha, beta, gamma)  blah (123, 456, 789)
(alpha, beta, gamma, delta)  blah (123, 456, 789, aaa)

After M-x my/merge-list

(alpha=123, beta=456, gamma=789)
(alpha=123, beta=456, gamma=789, delta=aaa)
like image 1
syohex Avatar answered Nov 11 '22 21:11

syohex