17

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.

Viktor Rosenfeld
  • 183
  • 1
  • 1
  • 6
  • 1
    Your code is fine. There is no similar system function. – sds Jun 22 '14 at 23:49
  • 1
    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`. – Stefan Jun 23 '14 at 13:39
  • Is there a way to make the code work with lexical scoping? – Viktor Rosenfeld Jun 24 '14 at 11:54

5 Answers5

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.

phils
  • 71,335
  • 11
  • 153
  • 198
  • Burps if the original list is nil! – Stefan Jun 23 '14 at 13:34
  • D'oh. Fixed. Thanks Stefan. – phils Jun 23 '14 at 16:44
  • 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? – Viktor Rosenfeld Jun 24 '14 at 11:58
  • 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. – phils Sep 24 '18 at 23:52
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)))
Jonathan Leech-Pepin
  • 7,684
  • 2
  • 29
  • 45
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.

gsg
  • 9,167
  • 1
  • 21
  • 23
2

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

(setq list1 (-union list1 list2))
Josh Cho
  • 87
  • 6
1

Sometimes I do this:

(setq l `(,@l i1 i2 i3))
memeplex
  • 2,297
  • 27
  • 26