Where parallels cross

Interesting bits of life

Merge plists in Elisp

Say that you have two property lists in Elisp (list :a 1 :b 3) and (list :a 2): you may need to merge them to obtain (list :a 2 :b 3). This operator exists in Clojure, but I didn't seem to find it nor in seq.el, nor dash.el nor in other Emacs' built-in libraries.

I also needed the operation to do a "deeper" merge:

(me-merge (list :andrea-favourites (list "food")) (list :andrea-favourites (list "books")))
; => (:andrea-favourites ("food" "books"))

And this is what I got to:

(defun me-keys (plist)
  "Return keys of PLIST."
  (--filter (and (symbolp it) (s-starts-with-p ":" (symbol-name it))) plist))
; (me-keys '(:a 1 :b 2))
; => (:a :b)

(defun me-merge (join-when-you-can? &rest plists)
  "Merge keys of PLISTS when possible.
If JOIN-WHEN-YOU-CAN? is true, if keys contain lists,
 we append their results instead of replacing."
  (--reduce
   (-reduce-from
    (lambda (acc1 key)
      (let ((a (plist-get acc key))
            (b (plist-get it key)))
        (if (and join-when-you-can? (listp a) (listp b))
            (append acc1 (list key (-union a b)))
          (append acc1 (list key b)))))
    nil
    (-union (me-keys it) (me-keys acc)))
   plists))
;(me-merge t '(:a ("1") :b "2") '(:a ("3") :b "3"))
; => (:a ("1" "3") :b "3")
;(me-merge nil '(:a "1" :b "2") '(:a "3" :b "3"))
; => (:a "3" :b "3")

Happy merging!

Comments