2

In an org tree consisting of hundreds of headings, how do I find out if there are any that are duplicates?

So, for instance, in the tree below,

* Heading 1
** some
** other
** headings
** some

I want to latch on to 'some'. I can sort them alphabetically to identify them manually. But with hundreds of headings, it is not practicable.

Also, can I control if it is a perfect match (everything under 'some' is exactly the same in both the instances) or just a heading match (we do not care about what is below 'some')?

This question is very similar to what I am asking. But unfortunately, there is no answer provided there. Although there are some hints in a comment.

3

Here's a function that collects all duplicate headings and their respective position in the buffer.

(defun collect-duplicate-headings ()
  (let (dups
        (hls (org-element-map (org-element-parse-buffer) 'headline
               (lambda (hl)
                 (cons (org-element-property :raw-value hl)
                       (org-element-property :begin hl))))))
    (dolist (hl hls)
      (when (> (cl-count (car hl) (mapcar #'car hls)
                         :test 'equal)
             1)
      (push hl dups)))
  (nreverse dups)))

You can use it with helm or ivy:

(defun show-duplicate-headings ()
  (interactive)
  (helm :sources (helm-build-sync-source "Duplicate headings"
                   :candidates (lambda ()
                                 (with-helm-current-buffer
                                   (collect-duplicate-headings)))
                   :follow 1
                   :action 'goto-char)))

To collect "perfect matches" use the following code instead:

(defun collect-duplicate-headings ()
  (let (dups
        contents
        (hls (org-element-map (org-element-parse-buffer) 'headline
               (lambda (hl)
                 (cons (org-element-property :raw-value hl)
                       (org-element-property :begin hl))))))
    (setq contents
          (cl-loop for hl in hls
                   for pos = (goto-char (cdr hl))
                   for beg = (progn pos (line-beginning-position))
                   for end = (progn pos (org-end-of-subtree nil t))
                   for content = (buffer-substring-no-properties beg end)
                   collect (list (car hl) (cdr hl) content)))
    (dolist (elt contents)
      (when (> (cl-count (last elt) (mapcar #'last contents)
                         :test 'equal)
            1)
     (push (cons (car elt)
                 (nth 1 elt))
           dups)))
 (nreverse dups)))
2

This function lists duplicate headings from the current buffer based on string identity. You can extend the storage structure to include more information about the contents of the text and test accodingly. Hints on how to do it can be found from: http://ergoemacs.org/emacs/elisp_parse_org_mode.html

(require 'dash) ; for -contains?

(defun my-print-duplicate-headings ()
  "Print duplicate headings from the current org buffer."
  (interactive)
  (with-output-to-temp-buffer "*temp-out*"
    (let ((header-list '())) ; start with empty list
      (org-element-map (org-element-parse-buffer) 'headline
        (lambda (x)
          (let ((header (org-element-property :raw-value x)))
            (when (-contains? header-list header)
              (princ header)
              (terpri))
            (push header header-list)))))))

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.