Delim Col: A handy tool for creating pretty tables, and converting those to different table formats


A Problem … and a Solution

You are given the following table 1

a       b       c       d
aaaa    bb      ccc     ddddd
aaa     bbb     cccc    dddd
aa      bb      ccccccc ddd

How would you generate this table

[ a   , b  , c      , d     ]
[ aaaa, bb , ccc    , ddddd ]
[ aaa , bbb, cccc   , dddd  ]
[ aa  , bb , ccccccc, ddd   ]

and this table

a       [ b  , c       ]        d
aaaa    [ bb , ccc     ]        ddddd
aaa     [ bbb, cccc    ]        dddd
aa      [ bb , ccccccc ]        ddd

and this table

[ <a>   , <b>  , <c>      , <d>     ]
[ <aaaa>, <bb> , <ccc>    , <ddddd> ]
[ <aaa> , <bbb>, <cccc>   , <dddd>  ]
[ <aa>  , <bb> , <ccccccc>, <ddd>   ]

and this table

a       [ <b>  , <c>       ]    d
aaaa    [ <bb> , <ccc>     ]    ddddd
aaa     [ <bbb>, <cccc>    ]    dddd
aa      [ <bb> , <ccccccc> ]    ddd

A answer is you use one of the Emacs’ built-in library called delim-col1.

What does delim-col do?

The delim-col package describes itself as

delim-col helps to prettify columns in a text region or rectangle

In my opinion, the above description doesn’t tell much about what it offers a user. Instead, I would describe the package as

delim-col creates pretty tables from a text region and helps convert those tables in to different table formats like TSV1, CSV1, Org1, LaTeX1, GFM1, 2   or any other makeshift format that you may come up with.

What does delim-col NOT do?

In doing the above conversions, you would expect that delim-col prompts you for all these parameters

  1. the regexp which separates each column
  2. a string to be inserted before (and after) each column
  3. a string to be inserted between each column
  4. a string to be inserted at the beginning (and end of) each row

and a way to add padding.

Unfortunately, the above parameters are hard-coded and aren’t gathered from the user on each run.

Step 1: Create a menu

Copy the Emacs Lisp snippet1 below to your .emacs and restart your Emacs.

This snippet does the following

      1. modifies the commands delimit-columns-region and delimit-columns-rectangle so that they prompt you for input on every run.
      2. adds a sub-menu named Extra Tools to the menu. To this sub-menu, it adds another sub-menu named Delimit Columns in ....
(defun my-delimits-column-region
    (orig-fun &rest args)
  (let
      ((delimit-columns-separator
        (read-regexp
         (format "%s (%s): " "Specify the regexp which separates each column" delimit-columns-separator)
         (list delimit-columns-separator)))
       (delimit-columns-before
        (read-string
         (format "%s (%s): " "Specify a string to be inserted before each column" delimit-columns-before)
         nil nil delimit-columns-before))
       (delimit-columns-after
        (read-string
         (format "%s (%s): " "Specify a string to be inserted after each column" delimit-columns-after)
         nil nil delimit-columns-after))
       (delimit-columns-str-separator
        (read-string
         (format "%s (%s): " "Specify a string to be inserted between each column" delimit-columns-str-separator)
         nil nil delimit-columns-str-separator))
       (delimit-columns-str-before
        (read-string
         (format "%s (%s): " "Specify a string to be inserted before the first column" delimit-columns-str-before)
         nil nil delimit-columns-str-before))
       (delimit-columns-str-after
        (read-string
         (format "%s (%s): " "Specify a string to be inserted after the last column" delimit-columns-str-after)
         nil nil delimit-columns-str-after))
       (delimit-columns-format
        (let*
            ((choices
              '(("Align Columns" . t)
                ("No Formatting")
                ("Align Separators" . separator)
                ("Pad Columns" . padding)))
             (default-choice
               (car
                (rassoc delimit-columns-format choices)))
             (choice
              (completing-read
               (format "%s (%s): " "Specify how to format columns" default-choice)
               choices nil t nil nil default-choice)))
          (message "%s" choice)
          (assoc-default choice choices))))
    (apply orig-fun args)))

(advice-add 'delimit-columns-region :around #'my-delimits-column-region)
(advice-add 'delimit-columns-rectangle :around #'my-delimits-column-region)

(define-key-after global-map
  [menu-bar extra-tools]
  (cons "Extra Tools"
        (easy-menu-create-menu "Extra Tools" nil))
  'tools)

(easy-menu-define my-delim-col-menu nil "Menu for Delim Col"
  '("Delimit Columns in ..."
    ["Region" delimit-columns-region :help "Prettify all columns in a text region"]
    ["Rectangle" delimit-columns-rectangle :help "Prettify all columns in a text rectangle"]
    "---"
    ["Customize" delimit-columns-customize :help "Customization of `columns' group"]))

(easy-menu-add-item (current-global-map) '("menu-bar" "extra-tools") my-delim-col-menu)

Step 2: Ensure that you have the Extra Tools submenu

Once you restart your Emacs, you would see an entry for Extra Tools in the menu bar.  If you aren’t seeing this submenu, repeat the previous step.

Step 3: Copy-paste the table in to an Emacs buffer

Copy-paste the input table either from this article or from the delim-col library 1.  It is important to note that the table scraped from this article will have spaces (and not tabs), but the one from delim-col will have tabs.

Step 4: Mark the region or rectangle

Step 5: Do the conversion

Screenshot from 2018-09-23 22-08-05.png

Emacs will prompt you for further input.   Key in the parameters as you deem fit.

The prompt string shows the factory default settings, and they are enclosed within  (). You can press Enter to accept the factory settings.

When copy-pasting the original table from this web article in to your Emacs, the table will contain spaces. So, when prompted with Specify the regexp which separates each column use   + (i.e. SPC+).

When prompted with Specify how to format columns you can press TAB , and choose among the candidates shown.  As you see from screenshot below, for my own run, I chose Align Separators.

Screenshot from 2018-09-23 22-28-54

Here is the output I get at the end of one such run.

Screenshot from 2018-09-23 22-33-24

Concluding Words

You can pose the original problem to experienced users of Emacs.  Each user will have his own suggestions.  Most users will suggest rectangle commands1.  Some users will suggest that you use org-table-convert-region1 to convert the original table in to Org-format table1, and create a makeshift code based on orgtbl-to-generic 1 (I bet, no user will suggest delim-col because it is a package which lacks an entry in the user manual.) None of the solutions you hear have the simplicity and quickness as delim-col.  In my opinion,  delim-col is a very useful tool to have in your toolkit.

Appendix

This article on delim-col was created as an aid to one of my friends1, a newcomer to Emacs. She wanted to convert a table scraped from web1 in to a Python dictionary1.

Specifically, she wanted to convert an HTML  table that looks like this

Screenshot-2018-9-23 Ensembl Stable ID prefixes

in to a python dictionary that looks like this

Screenshot from 2018-09-24 00-04-58

With a little imagination, it is easy to see how she can use delim-col to achieve much of what she wants.

That said, I hear those of you asking me “What if the second column in the friend’s table happened to be numeric, and she didn’t want the numeric fields quoted …”.  In this case, I suppose, delim-col doesn’t have anything immediate to offer, and I am afraid, the friend needs to switch to other means.

Advertisements

1 thought on “Delim Col: A handy tool for creating pretty tables, and converting those to different table formats

  1. Very nice. Would you please consider converting this to a patch to delim-col.el (I think make it so that a prefix argument C-u triggers the prompts), and submitting it with M-x report-emacs-bug ? This seems like an obvious enhancement to make upstream.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this:
search previous next tag category expand menu location phone mail time cart zoom edit close