2

I want to implement an org-agenda-cmp-user-defined for use with org-agenda-sorting-strategy and user-defined-up / user-defined-down.

In order to do that, I have two lists of tags I need to compare, from the two items to be sorted passed to org-agenda-cmp-user-defined. I want the function to compare based on a particular order of tags, say:

("spam" "ham" "eggs")

So that the items are sorted by whether they have tag "spam" first, then breaking ties by whether they have tag "ham", etc.

How can I do that, in good elisp style? I'd like it to be reasonably CPU-efficient, since org-agenda already does quite a bit of work building its views.

I'm aware that org agenda has a built-in way of sorting based on the first tag on each item: note this is different from that behaviour.

1

Dunno anything about org-agenda-cmp-user-defined etc. But if the question is just about comparing two equal-length lists of strings, in order, until the first difference is found, and returning -1 if the string from the first list is less than that of the second list, returning 1 if the reverse is true, and returning nil if there is no difference, then this should do the job:

(defun my-agenda-cmp (list1 list2)
  "Compare two same-length lists of strings, LIST1 and LIST2.
Compare strings from each, in order.
Return nil if they have the same strings in the same order.
Otherwise, for the first difference encountered, return:
 -1 if LIST1's string is less than LIST2's string.
  1 if LIST2's string is less than LIST1's string."
  (let* ((l1  list1)
         (l2  list2)
         (x1  (car l1))
         (x2  (car l2))
         cmp)
    (catch 'my-agenda-cmp
      (while l1
        (setq cmp  (compare-strings x1 nil nil x2 nil nil))
        (cond ((eq t cmp)
               (setq l1  (cdr l1)
                     l2  (cdr l2)
                     x1  (car l1)
                     x2  (car l2)))
              ((> cmp 0) (throw 'my-agenda-cmp  1))
              ((< cmp 0) (throw 'my-agenda-cmp -1))))
      nil)))

On the other hand, if the two lists are not guaranteed to be of equal length then this should do the job:

(defun my-agenda-cmp (list1 list2)
  "Compare two lists of strings, LIST1 and LIST2.
Return -1 if LIST1 is shorter.  Return 1 if LIST2 is shorter.
Otherwise, compare strings from each, in order.

Return nil if they have the same strings in the same order.
Otherwise, for the first difference encountered, return:
 -1 if LIST1's string is less than LIST2's string.
  1 if LIST2's string is less than LIST1's string."
  (let* ((l1  list1)
         (l2  list2)
         (x1  (car l1))
         (x2  (car l2))
         cmp)
    (catch 'my-agenda-cmp
      (while (and x1  x2)
        (setq cmp  (compare-strings x1 nil nil x2 nil nil))
        (cond ((eq t cmp)
               (setq l1  (cdr l1)
                     l2  (cdr l2)
                     x1  (car l1)
                     x2  (car l2)))
              ((> cmp 0) (throw 'my-agenda-cmp  1))
              ((< cmp 0) (throw 'my-agenda-cmp -1))))
      (and (or  x1  x2)                 ; same length  => nil
           (and x1  1)                  ; L1 is longer =>  1
           -1))))                       ; L2 is longer => -1

In both versions the main part of the code just goes down the lists calling compare-strings. As soon as the first difference is encountered the function returns the result.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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