Org Mode: calculating table sums using tag hierarchies
| org, elispWhile collecting posts for Emacs News, I came across this question about adding up Org Mode table data by tag hierarchy, which might be interesting if you want to add things up in different combinations. I haven't needed to do something like that myself, but I got curious about it. It turns out that you can define a tag hierarchy like this:
The first two lines remove any other tags you've
defined in your config aside from those in
org-tag-persistent-alist
, but can be omitted if
you want to also include other tags you've defined
in org-tag-alist
. Note that it doesn't have to
be a strict tree. Tags can belong to more than one
tag group.
EduMerco wanted to know how to use those tag
groups to sum up rows in a table. I added a
#+NAME
header to the table so that I could refer
to it with :var source=source
later on.
| tag | Q1 | Q2 |
|------+----+----|
| tagA | 9 | |
| tagB | 4 | 2 |
| tagC | 1 | 4 |
| tagD | | 5 |
| tagE | | 6 |
(defun my-sum-tag-groups (source &optional groups)
"Sum up the rows in SOURCE by GROUPS.
If GROUPS is nil, use `org-tag-groups-alist'."
(setq groups (or groups org-tag-groups-alist))
(cons
(car source)
(mapcar
(lambda (tag-group)
(let ((tags (org--tags-expand-group (list (car tag-group))
groups nil)))
(cons (car tag-group)
(seq-map-indexed
(lambda (colname i)
(apply '+
(mapcar (lambda (tag)
(let ((val (or (elt (assoc-default tag source) i) "0")))
(if (stringp val)
(string-to-number val)
(or val 0))))
tags)))
(cdr (car source))))))
groups)))
Then that can be used with the following code:
#+begin_src emacs-lisp :var source=source :colnames no :results table
(my-sum-tag-groups source)
#+end_src
to result in:
tag | Q1 | Q2 |
GT1 | 10 | 9 |
GT2 | 4 | 8 |
GT3 | 5 | 11 |
Because org--tags-expand-group
takes the groups
as a parameter, you could use it to sum things by
different groups. The #+TAGS:
directives above set
org-tag-groups-alist
to:
(("GT1" "tagA" "tagC" "tagD")
("GT2" "tagB" "tagE")
("GT3" "tagB" "tagC" "tagD"))
Following the same format, we could do something like this:
(my-sum-tag-groups source '(("Main" "- Subgroup 1" "- Subgroup 2")
("- Subgroup 1" "tagA" "tagB")
("- Subgroup 2" "tagC" "tagD")
))
tag | Q1 | Q2 |
Main | 14 | 11 |
- Subgroup 1 | 13 | 2 |
- Subgroup 2 | 1 | 9 |
I haven't specifically needed to add tag groups in tables myself, but I suspect the recursive expansion in org--tags-expand-group
might come in handy even in a non-Org context. Hmm…