29

What's the best way to loop over an alist and do something with each pair in Emacs Lisp? I suppose a macro wouldn't be difficult, I'm just wondering if this is built in somewhere. Is there a more elegant way than below?

(setq my-list '((a . 1)
                (b . 2)
                (c . 3)))

(loop for key in (mapcar 'car my-list)
      for value in (mapcar 'cdr my-list)
      collect (cons value key))

;; Returns this
((1 . a)
 (2 . b)
 (3 . c))
Joe
  • 3,370
  • 4
  • 33
  • 56

4 Answers4

31

cl-loop from cl-macs.el has support for destructing like CL:

(cl-loop for (key . value) in my-list
      collect (cons value key))
TheJJ
  • 931
  • 12
  • 21
Jürgen Hötzel
  • 18,997
  • 3
  • 42
  • 58
  • 6
    Now that `cl` is deprecated, the modern equivalent of this is to use the identically-featured `cl-loop` macro from `cl-lib`. That library is built into newer emacs versions and a backwards-compatible package is available from GNU ELPA. – sanityinc Jul 01 '16 at 08:25
  • 1
    What does "collect" mean here? – Benilda Key Sep 15 '20 at 20:22
19

Another way to do this without loop is with mapcar and a lambda. I don't think it's any more elegant, but it's more idiomatic for elisp as opposed to Common Lisp:

(mapcar (lambda (element)
      (let ((key (car element))
            (value (cdr element)))
      (cons value key)))
      '((1 . a) (2 . b)))
ataylor
  • 64,891
  • 24
  • 161
  • 189
19

It's not clear what exactly you want to do -- the question is very general. There are many ways to loop over an alist and act on some or all of its entries. You show one way yourself. Look also at while and, in particular, dolist. This is your example using dolist:

    (let ((res  ()))
     (dolist (x my-list)
       (push (cons (cdr x) (car x)) res))
     (nreverse res))

(There is probably a better way to use loop than in your example -- no need to build three lists (two mapcars + loop), for instance.)

Drew
  • 29,895
  • 7
  • 74
  • 104
7

dash.el provides many list manipulation functions like maps. Those that accept functions as arguments are also have anaphoric counterparts. This allows for terse functional code. dash.el functions start with a dash, hence the name, and anaphoric versions start with 2 dashes. So ataylor's variant would look much better like this:

(--map (cons (cdr it) (car it)) '((1 . a) (2 . b))) ; ((a . 1) (b . 2))
Community
  • 1
  • 1
Mirzhan Irkegulov
  • 17,660
  • 12
  • 105
  • 166