org-map-entries API to loop through selected or all
headings in an Org file.
I got a lot of Org-Mode headings and I want to modify their properties (add, remove, edit). Is there a function to do the same modifications on each heading?
I think that the best solution to that question is using the org-map-entries function.
But somehow when replying to that question then, that wasn’t what
first came to my mind! .. when ironically that function is the main
function that enables my preferred subtree-based flow in
😆. So I am writing this post to better ingrain the following
concept in myself ..
If you need to loop through headings in an Org buffer, and especially if you need to modify that buffer in the process, use org-map-entries1.
- I will give a give introduction to the
- Then provide a super-short solution to the above question.
org-map-entries API #
I will give only a broad level overview on how to use this function. I would encourage the reader to refer to the resources at the end of this post to learn more about it.
So let’s start by looking at this function’s signature:
(org-map-entries FUNC &optional MATCH SCOPE &rest SKIP)
org-map-entries function iterates through all the headings
meeting the MATCH criteria in the determined SCOPE, and then calls
the specified function FUNC at each of those headings.
FUNCfunction accepts no arguments and is called at the beginning of each Org heading.
- The optional second argument MATCH is either nil,
tor a search string.
- If MATCH is nil or
t, all headings will be visited by the iteration and FUNC will be called on all of them.
- But if MATCH is a string, the headings will first be filtered based on that string and then the FUNC will be called on only those.
- If MATCH is nil or
- For explanations on the optional SCOPE and SKIP arguments, see
Org Info: Using the Mapping API or C-h f
org-map-entriesfrom within Emacs.
Here’s a typical
org-map-entries call that loops through all the
headings in the visible buffer:
(org-map-entries #'some-function) where all the optional
argument values are nil. Next, we’ll see some examples of
string-type MATCH arguments used for filtering the headings.
MATCH strings #
Below table shows few examples of match string patterns.
|Regexp matching tags|
|Tag set intersection|
|Tag set difference|
|Tag set union or boolean OR|
|Tag set intersection or boolean AND|
|Specified property value matching a string|
|Specified property value not matching a string|
|Specified property value matching a regexp|
|Specified property value compared with a numeric value|
|Check value of headline’s special property LEVEL|
|Check value of headline’s TODO state|
Comparison Types #
- If the comparison value is a plain number, a numerical comparison is done, and the allowed operators are ‘<’, ‘=’, ‘>’, ‘<=’, ‘>=’, and ‘<>’.
- If the comparison value is enclosed in double quotes, a string comparison is done, and the same operators are allowed.
- If the comparison value is enclosed in curly braces, a regexp match is performed. For this comparison, only ‘=’ (regexp matches) and ‘<>’ (regexp does not match) operators are allowed.
- Comparison with dates and Group Tags is also possible. See Org Info: Matching tags and properties for more details.
Other notes #
- The property names are case-insensitive. So these all work the
- The “tag” and “property” matches can be mixed up using the boolean
+’ and ‘
-’ operators. So searching ‘
+LEVEL=3+boss-TODO="DONE"’ lists all level three headlines that have the tag ‘boss’ and are not marked with the TODO keyword ‘DONE’.
&’ binds more strongly than ‘
- Grouping of match expressions using parentheses is not supported.
Example: Modifying a property in all headings #
Below is an example solution to the Mastoson question that I referenced in the beginning of this post.
(defun test/set-property-at-heading () "Function to be called at the beginning of an Org heading." (let ((el (org-element-at-point))) (org-set-property "foo" (org-element-property :title el)))) (org-map-entries #'test/set-property-at-heading)
- It defines a function that parses the Org element at point using
org-element-at-point, gets the
titleproperty of the element This function is designed to be called by
org-map-entriesand so the point at the time of calling this function will always be on a heading. , and sets that to the headline element’s
org-map-entriescall now simply calls this function on each heading in the visible scope of the Org buffer.
org-map-entries References #
Org mode has another popular mapping/looping API function org-element-map. I won’t go into much detail about that in this post — I’ll just mention that
org-element-mapis not the best choice if you need to modify the original Org buffer. It’s main use is to loop through a parsed AST of an Org buffer and optional modify those elements in memory. ↩︎