I have a table in org-mode which has the following format:

| Name       | email           | number |
| Doe, John  | jod@example.org |      7 |
| Doe, Jane  | jod@example.org |        |
| Foo, Pete  | pf@example.com  |      5 |
| Bar, Mary  | maba@  |      3 |
| Quux, Mike | bz@ |        |

But I want the table to be formatted to look like this:

| Name       | email           | number |
| Bar, Mary  | maba@  |      3 |
| Quux, Mike | bz@ |        |
| Doe, John  | jod@example.org |      7 |
| Doe, Jane  | jod@example.org |        |
| Foo, Pete  | pf@example.com  |      5 |

This table should be sorted alphabetically. So the last two rows should be the first. The separation with the horizontal lines should stay. If there are different words in in grouped columns, it doesn't matter how they are sorted. So the last group in the table above can either be Bar followed by Baz or Baz followed by Bar. How can I do this with Emacs org-mode?

  • 1
    You should add an example on how you want the table to look, like: "I want this... To look like this...". – Luke Shimkus Oct 27 '14 at 23:07
  • 2
    I don't really understand the sorting rules. Do you only want to sort on the first column within each section and then sort the sections dependent on the "name" column of the first row of each section? – rekado Oct 28 '14 at 14:29

Unfortunately, org-mode does not keep a data structure around that could be used to perform modifications that are then reflected directly in the table, so we have to slurp up the lines into a list, do some list processing, and then convert the resulting list back into text.

In general the approach is this:

  • compute the group sizes from information in org-table-hlines; this variable holds the row numbers of horizontal lines
  • store the lines relating to each group in a list and put each group in a list

    '(("| Doe, John...|" "| Doe, Jane...|")
      ("| Foo, Pete...|")
      ("| Bar, Mary...|" "| Quux, Mike...|"))
  • sort this list by the value of the car of each item, getting something like this:

    '(("| Bar, Mary...|" "| Quux, Mike...|")
      ("| Doe, John...|" "| Doe, Jane...|")
      ("| Foo, Pete...|"))
  • then rebuild the table from this list by iterating over each group


Here's something to get you started. It's rather awkward, but it's just to illustrate the idea. (rows-by-group actually contains duplicates, so this is not completely correct.)

(defun my-org-sort-lines ()
  (org-table-recalculate 'iterate)
  (let* ((hlines org-table-hlines)
         (rows-by-group (let* ((xs (cdr (append hlines nil)))
                               (xs2 (mapcar #'1+ xs))
                               (ys (mapcar #'1- (cdr (copy-sequence xs)))))
                          (mapcar* #'list (setcdr (last xs2) xs2) ys)))
         ;; fetch rows and store as nested list
         (groups (mapcar (lambda (group)
                           (mapcar (lambda (row-id)
                                     (goto-line row-id)
                                     (let ((beg (point)))
                                       (buffer-substring beg (point))))
         ;; sort groups by first row
         (sorted (sort groups (lambda (a b)
                                (string-lessp (car a) (car b))))))

    ;; remove existing table rows and rebuild from group list
    (goto-char (org-table-begin))
    (next-line 1)
    (kill-region (point) (org-table-end))
    (mapcar (lambda (group)
              (insert "|---")(org-table-recalculate)(next-line)
              (mapcar (lambda (row)
                        (insert row)(newline)) group))
  (insert "|---")
| improve this answer | |
  • Interesting. Been thinking about how to modify tables programmatically and this is a nice start. – undostres Nov 22 '14 at 17:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.