How to append multiple elements to a list in Emacs lisp
Asked Answered
I

5

19

In Emacs lisp there is add-to-list to add a single element to a list (if it doesn't exist already).

Instead of one, I want to add multiple elements. Also, I do not want to filter duplicate elements but add them to the list nonetheless.

Currently, I have implemented the following function:

(defun append-to-list (list-var elements)
  "Append ELEMENTS to the end of LIST-VAR.

The return value is the new value of LIST-VAR."
  (set list-var (append (symbol-value list-var) elements)))

The function does what I want but I was wondering if something like this (or better) already exists in Emacs lisp. I don't want to reinvent the wheel.

Update 1: Stefan points out below that this code does not work with lexical scoping. Is there a way to make it work?

Update 2: Previously I thought that duplicate filtering would be fine but it's not. I do need the duplicates.

Irresoluble answered 22/6, 2014 at 22:33 Comment(3)
Your code is fine. There is no similar system function.Loblolly
I wouldn't describe it as "fine", but yes, it will work. Using symbol-value and set means it can't be used with a lexically scoped variable. Unless you really need it, better add elements at the beginning, since elements will almost always be shorter (and sometimes much shorter) than list-var.Thenceforward
Is there a way to make the code work with lexical scoping?Irresoluble
C
11

This would be almost equivalent1 but faster, as it does not make a copy of the original list before appending the new elements.

(defun append-to-list (list-var elements)
  "Append ELEMENTS to the end of LIST-VAR.

The return value is the new value of LIST-VAR."
  (unless (consp elements)
    (error "ELEMENTS must be a list"))
  (let ((list (symbol-value list-var)))
    (if list
        (setcdr (last list) elements)
      (set list-var elements)))
  (symbol-value list-var))

1 append does not copy the final element, but uses it directly as the tail of the new list, so that part is identical. However if there are additional references to the original list object (or some part thereof), then there will be a functional difference between copying that list (via append), and just extending it (with setcdr). Which of those two outcomes you actually want is up to you, of course.

Ceruse answered 23/6, 2014 at 0:44 Comment(4)
Burps if the original list is nil!Thenceforward
D'oh. Fixed. Thanks Stefan.Ceruse
At least in my current use case I do not keep references around. So this looks cleaner to me because it avoids copying. Thanks! But can it be made to work with lexical scoping?Irresoluble
No, you can't do this with lexical scoping when the list-var argument is a symbol, because you've already lost access to the lexical value by doing that. You would have to pass the list value and manipulate that directly.Ceruse
B
8

I have the following in my init file that allows for adding multiple elements. I don't know how efficient it is to loop through the items to add but it prevents duplicate elements.

(defun jlp/add-to-list-multiple (list to-add)
  "Adds multiple items to LIST.
Allows for adding a sequence of items to the same list, rather
than having to call `add-to-list' multiple times."
  (interactive)
  (dolist (item to-add)
    (add-to-list list item)))
Bolick answered 23/6, 2014 at 19:33 Comment(2)
This won't work because I actually do need duplicate values in the list.Irresoluble
If you need duplicates, you can use push instead of add-to-list.Seersucker
S
3

If you don't care about ordering:

(setf var (cl-list* elt1 elt2 elt3 var))

The last argument to list* becomes the tail of the resulting list.

Sontich answered 23/6, 2014 at 7:20 Comment(0)
P
3

You can also use dash.el if you want to filter duplicate elements.

(setq list1 (-union list1 list2))
Prediction answered 13/7, 2021 at 18:35 Comment(0)
P
1

Sometimes I do this:

(setq l `(,@l i1 i2 i3))
Pokpoke answered 31/7, 2022 at 18:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.