32

I have an alist in emacs lisp like:

(setq a1
 '((:k1 . 1)
   (:k2 . 2)
   (:k3 . 3)))

and i want to change value of :k1 to 10, like (:k1 . 10). How do i do that?

I tried (setf (assoc :k1 a1) '(:k1 . 10)) - it didn't work.

Mirzhan Irkegulov
  • 17,660
  • 12
  • 105
  • 166

6 Answers6

22

With alists, you usually add a new cons in front of the old one to "shadow" the old value, like so:

(add-to-list 'a1 '(:k1 10))

After you do this (assoc :k1 a1) will return 10.

If you want to "undo" your change so assoc again returns your old value, use this code:

(setq a1 (delq (assoc :k1 a1) a1))

This will remove the FIRST match for :k1 from a1.

krzysz00
  • 2,083
  • 12
  • 24
17

As of Emacs 25.1, alist-get is a place form so you can do:

(setf (alist-get :k1 a1) 10)
Dale
  • 572
  • 4
  • 15
7

The setf macro doesn't know about assoc, but you could still use that approach in a slightly more manual fashion:

(let ((item (assoc :k1 a1)))
  (setf (car item) :k1)
  (setf (cdr item) 10))

and if all that is required is to set the cdr for the given car (rather than replacing both), then we can simplify this to just:

(setf (cdr (assoc :k1 a1)) 10)
phils
  • 71,335
  • 11
  • 153
  • 198
  • It's probably not necessary to set the car to :k1, as that's already the value. As a general rule, it's probably better to push new values onto assoc lists than modify them, but it all depends on why one is trying to set a new value. – Vatine Apr 08 '12 at 16:18
  • 1
    Agreed, but as I was adapting the code in the question, and that did set the car, I included that here as well. Of course if setting the car is not desired, then the code can be simplified, so I'll add that alternative. – phils Apr 08 '12 at 16:34
6

This shorter and simpler version works in Emacs Lisp (MAC Lisp) for me:

(setcdr (assoc :k1 a1) 10)

or, if you happen to be using Common Lisp:

(rplacd (assoc :k1 a1) 10)

(Note that setcdr and rplacd may behave slightly different when it comes to which value they return.)

Liman
  • 61
  • 1
  • 2
1

What about assq-delete-all:

(setq sql-product-alist
      (cons '(ms-tsql :server ....)
            (assq-delete-all 'ms-tsql sql-product-alist)))
gavenkoa
  • 45,285
  • 19
  • 251
  • 303
1

Here's a function based on gavenkoa's suggestion -

(defun alist-set (alist-symbol key value)
  "Set KEY to VALUE in alist ALIST-SYMBOL."
  (set alist-symbol
        (cons (list key value) 
              (assq-delete-all key (eval alist-symbol)))))

usage -

(let ((foo '((a 1) (b 2))))
  (alist-set 'foo 'a 3)
  foo) ; => ((a 3) (b 2))
Brian Burns
  • 20,575
  • 8
  • 83
  • 77