# Map a function across a property list?

Q: what is the idiomatic way to map a function across a property list?

The various mapping functions (`mapcar` and family) map a function over a sequence such as a list. How does one use these functions when dealing with a property list, i.e., when trying to map across each of the properties contained in the list (which would be every other element starting from the first one)? It seems to me that the mapping function would need to access the list in pairs of elements rather than as individual elements.

As a toy example, how would one take a property list and collect all of the property values? If it were an association list instead it would be pretty simple:

``````(mapcar #'cadr '((:prop1 a) (:prop2 b) (:prop3 c))) ;=> (a b c)
``````

I'm sure this could be done with a loop, but it seems a little laborious and I'm wondering if there's a more idiomatic way to do it.

• Please clarify whether you want to map over only the property values (which is what it sounds like, and which is what your `mapcar` alist example does) or you want to map over the pairs of property symbol and property value. The latter is more general (more generally useful), I would guess.
– Drew
Apr 10 '15 at 21:38
• @Drew: I'm more interested in the general case; the example was the simplest one I could think of. I'd like to know how to map over the property/value pair. If the answer is "a loop," then so be it, but I'm wondering if there's a more elegant solution.
– Dan
Apr 10 '15 at 21:54
• What you have in your example isn't a property list. Property lists have even number of elements, odd elements are property names, even elements are property values. What you have would be, probably, called a tree (a list of lists). Apr 11 '15 at 8:54

You will likely get various `loop` and iteration answers. AFAIK, there is no idiomatic way to do this. I don't even think it's very common to want to accumulate only the property values (not associating them with the properties).

Here is one simple way to do it:

``````(defun prop-values (plist)
"..."
(let ((pl    (cdr plist))
(vals  ()))
(while pl
(push (car pl) vals)
(setq pl  (cddr pl)))
(nreverse vals)))
``````

For the more general case, where you want to map a binary function over a plist:

``````(defun map-plist (fn plist)
"..."
(let ((pl    plist)
(vals  ()))
(while pl
(push (funcall fn (car pl) (cadr pl)) vals)
(setq pl (cddr pl)))
(nreverse vals)))

(setq foo '(a 1 b 2 c 3 d 4 e 5))

(map-plist #'cons foo) ; => ((a . 1) (b . 2) (c . 3) (d . 4) (e . 5))
``````

This would probably depend on a situation. In general, if I need to tie a number of values with a number of names, I'd use a hash-table, but if I have to use a property list, I'd use `cl-loop`. Below are some examples:

``````(cl-loop for (key value) on '(:prop1 a :prop2 b :prop3 c) by 'cddr
collect value)
;;; (a b c)
``````

And if you have a data-structure you show in your example:

``````(cl-loop for (key value) in '((:prop1 a) (:prop2 b) (:prop3 c))
collect value)
;;; (a b c)
``````

With Emacs from the current Git HEAD which will become Emacs 25, you can do the following, i.e., turn the plist into an alist easily with the new `seq-partition` function, and then process the alist entries using standard `mapcar`.

``````(let* ((my-plist (list :a 1 :b 2 :c 3 :more (list 4 5 6)))
(my-alist (seq-partition my-plist 2))
(my-reverse-alist (mapcar (lambda (entry)
(let ((prop (car entry))
(list val prop)))
my-alist)))
(message "my-plist: %s\nmy-alist: %s\nmy-reverse-alist: %s"
my-plist my-alist my-reverse-alist))
;; my-plist: (:a 1 :b 2 :c 3 :more (4 5 6))
;; my-alist: ((:a 1) (:b 2) (:c 3) (:more (4 5 6)))
;; my-reverse-alist: ((1 :a) (2 :b) (3 :c) ((4 5 6) :more))
``````

Have a look at the `seq-partition` docs using `C-h f seq-partition RET`.

To me the answer is to turn the plist into an alist, which is an equivalent data-structure, just half as deep and easier to manipulate.

• Well, that's kind of an answer (and good advice), but more of a comment.
– Drew
Apr 10 '15 at 21:54

One idea is to use `-map-indexed` from `dash` and apply transformation only to odd values of the list:

``````(-non-nil (-map-indexed (lambda (index item) (when (oddp index) item))
'(a x b y c z))) ; => (x y z)
(-non-nil (--map-indexed (when (oddp it-index) it) '(a x b y c z))) ; => (x y z)
``````

Another idea is to just convert a plist to a hash-table, using `ht<-plist` from `ht`:

``````(ht-values (ht<-plist '(a x b y c z) 'equal)) ; (z y x)
``````

Notice that hash-table doesn't preserve the order of items.