boechat107
boechat107

Reputation: 1724

Common Lisp - Is there a built-in function to filter a plist by keys?

I'm looking for a built-in function like the pfilter-by-keys below:

(pfilter-by-keys '(:a :b) '(:c 10 :b 20 :a 4))
;; => (:B 20 :A 4)

Its code is pretty simple:

(defun pfilter-by-keys (keys plist)
  "List -> PList -> PList
  Returns a new plist with only the keys/values correspondent to the given
  keys."
  (loop for (k v) on plist by #'cddr
        when (member k keys :test #'equal)
          append (list k v)))

Does CL has some built-in function like the one above?

PS.: Alexandria has a very close function: remove-from-plist.

Upvotes: 1

Views: 1546

Answers (2)

Dan Robertson
Dan Robertson

Reputation: 4360

There is no function to do this (and this isn’t the sort of thing one usually wants to do)

There is a macro remf which removes a key from a plist in some place.

Another way you might achieve this is by:

(destructuring-bind (&key (a nil ap) (b nil bp) &allow-other-keys) plist
  (append (if ap (list :a a)) (if bp (list :b b))))

But note that this only works if you already know what keys you want to keep and this does not preserve the order in the plist and it does not preserve repeated keys (i.e. if your plist contains the key :a multiple times the result will contain it only once).

You can modify this for non keyword keys using the normal lambda list syntax for general (symbol) keys.

Upvotes: 5

Rainer Joswig
Rainer Joswig

Reputation: 139381

CL:GET-PROPERTIES is a building block:

(defun keep-properties (plist indicator-list &aux k v)
  "Keeps all property list entries for a given indicator-list."
  (loop do (setf (values k v plist)
                 (get-properties plist indicator-list))
        while plist
        collect k collect v
        do (pop plist) (pop plist)))

Note, that it is better to collect twice, than to append/list in a LOOP.

Upvotes: 5

Related Questions