Is there a way to replace an item in a list?
(replace-element-in-list (elem-src elem-dst ls &optional times comparison-fn))
;; ...
)
Example use:
(replace-element-in-list 1 100 '(3 2 1)) => '(3 2 100)
Is there a way to replace an item in a list?
(replace-element-in-list (elem-src elem-dst ls &optional times comparison-fn))
;; ...
)
Example use:
(replace-element-in-list 1 100 '(3 2 1)) => '(3 2 100)
Here is a non destructive version.
(defun replace-element-in-list (elem-src elem-dst ls &optional times comparison-fn)
(setq times (or times (length ls)))
(mapcar
(lambda (item)
(cond
((and (> times 0)
(funcall (or comparison-fn #'eq) item elem-src))
(cl-decf times)
elem-dst)
(t
item)))
ls))
(replace-element-in-list 2 "2" '(1 2 3 2 4))
;; => (1 "2" 3 "2" 4)
(replace-element-in-list 2 "2" '(1 2 3 2 4) 1)
;; => (1 "2" 3 2 4)
(replace-element-in-list "2" 2 '("1" "2" "3" "2" "4") nil #'string=)
;; => ("1" 2 "3" 2 "4")
(length ls)
seems unnecessary, could use most-positive-fixnum
instead, or allow it to be nil
.
Commented
Sep 6, 2020 at 10:21
(require 'cl-lib)
(cl-substitute 100 1 '(3 2 1))
gives (3 2 100)
.
(cl-substitute 100 1 '(3 2 1 2 1 2 1) :count 2)
gives (3 2 100 2 100 2 1)
.
(cl-substitute 100 "one" '("three" "two" "one") :test #'string-equal)
gives ("three" "two" 100)
.
Furthermore, there are the keyword arguments :start
, :end
, and :from-end
. See the doc.
The question doesn't specify what "replacement" means. Does it mean create and return a new list that is like the original one except for the indicated parts? Or does it mean replace in place, that is, modify the original list?
Here is an example that modifies the original list, i.e., a "destructive" operation.
(defun replace-element-in-list (old new xs)
(let ((tail (member old xs)))
(while tail
(setcar tail new)
(setq tail (member old tail))))
xs)
(replace-element-in-list 1 100 (list 3 1 2 1)) ; => (3 100 2 100)
If you want an optional comparison-function
parameter, then use (funcall comparison-function ...)
instead of (member...)
, and default it to member
.
If you want an optional times
parameter then initialize a local counter to 0
and compare it to times
in the while
test etc.
(defun replace-element-in-list (old new xs &optional times compare-fn)
(let ((count 0)
tail)
(setq compare-fn (or compare-fn #'member)
tail (funcall compare-fn old xs))
(while (and tail (or (not times)
(<= (setq count (1+ count)) times)))
(setcar tail new)
(setq tail (funcall compare-fn old tail))))
xs)
(replace-element-in-list 1 100 (list 3 1 2 1 5 1 1 6))
; => (3 100 2 100 5 100 100 6)
(replace-element-in-list 1 100 (list 3 1 2 1 5 1 1 6) 3)
; => (3 100 2 100 5 100 1 6)
(setq x (list 1))
(replace-element-in-list x 100 (list 3 '(1) 2 x 5 (copy-sequence x) x 6) nil #'memq)
; => (3 (1) 2 100 5 (1) 100 6)
(replace-element-in-list x 100 (list 3 '(1) 2 x 5 (copy-sequence x) x 6) 1 #'memq)
; => (3 (1) 2 100 5 (1) (1) 6)