Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapcar and assoc

I would like to do:

(mapcar #'assoc '(a s) '((a . b) (c . d) (s . f)))

and have it return

((A . B) (S . F))

Which seems pretty reasonable, considering (assoc 'a '((a . b) (c . d) (s . f))) returns (A . B) and (assoc 's '((a . b) (c . d) (s . f))) returns (S . F). But alas it does not work:

*** - ASSOC: A is not a list
The following restarts are available:
ABORT          :R1      Abort main loop

Any thoughts?

like image 685
rhombidodecahedron Avatar asked Jan 29 '11 23:01

rhombidodecahedron


2 Answers

When used with two lists, mapcar applies the function pair-wise to the lists (and with three lists it applies them triple-wise etc.). So

(mapcar #'assoc '(a s) '((a . b) (c . d) (s . f)))

is the same as

( (assoc 'a (a . b)) (assoc 's (c . d)) )

(when used with lists of different length, mapcar uses the size of the smallest list). To get what you want, you should do:

(mapcar (lambda (x) (assoc x '((a . b) (c . d) (s . f)))) '(a s))
like image 169
sepp2k Avatar answered Sep 17 '22 18:09

sepp2k


We need another list level. The second argument should be a list of assoc lists.

CL-USER >  (mapcar #'assoc '(a s) '(((a . b) (c . d) (s . f))))

((A . B))

But the second argument is only one element long. Now we can use a trick and make it a circular list:

CL-USER > (mapcar #'assoc '(a s) '#1=(((A . B) (C . D) (S . F)) . #1#))

((A . B) (S . F))

If we construct a circular list for the second argument, then it works.

As a function:

(defun circular (list)
  (if (null list)
      list
    (setf (cdr (last list)) list)))

CL-USER > (mapcar #'assoc '(a s) (circular '(((a . b) (c . d) (s . f)))))

((A . B) (S . F))
like image 20
Rainer Joswig Avatar answered Sep 19 '22 18:09

Rainer Joswig