8

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@127.0.0.1  |      3 |
| Quux, Mike | bz@192.168.12.1 |        |
|------------+-----------------+--------|

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

| Name       | email           | number |
|------------+-----------------+--------|
| Bar, Mary  | maba@127.0.0.1  |      3 |
| Quux, Mike | bz@192.168.12.1 |        |
|------------+-----------------+--------|
| 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?

2
  • 1
    You should add an example on how you want the table to look, like: "I want this... To look like this...". Oct 27, 2014 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?
    – user2005
    Oct 28, 2014 at 14:29

1 Answer 1

4

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

EDIT:

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 ()
  (interactive)
  (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)
                                     (beginning-of-line)
                                     (let ((beg (point)))
                                       (end-of-line)
                                       (buffer-substring beg (point))))
                                   group))
                           rows-by-group))
         ;; 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))
            sorted))
  (insert "|---")
  (org-table-recalculate))
2
  • Interesting. Been thinking about how to modify tables programmatically and this is a nice start.
    – undostres
    Nov 22, 2014 at 17:06
  • Ugh, why isn't this built in…
    – dremodaris
    Jan 3, 2022 at 19:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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