10

What is the proper way to add many items to a list? I assume just using many add-to-lists is not the normal way?

1
  • 1
    The question is not very clear. In what form are you given the items to begin with? Are they already in some (other) list? or vector? What do you mean by "proper" - what are you expecting/preferring? What are your concerns about the different ways of adding items to a list? Do you care whether the list is the value of a global variable, and you want to be sure the variable's value is updated? Do other lists share list structure with the list?
    – Drew
    Jan 10, 2018 at 15:27

3 Answers 3

7

There are many different ways. Not sure if there's a particularly "proper" way. There are considerations you should take depending on how you want to add it (destructively, non-destructively) or if you care a lot about efficiency.

original list (1) things you want to add (2 3 4)

There are many different ways of adding. Things can be added destructively, meaning the original set of numbers is modified or non-destructively, just generating a new list. To see more look at list modification.

This is non-destructive. (append '(1) '(2 3 4)) ;=> '(1 2 3 4)

This is destructive and more indirect (more of a replacement than addition), here it replaces the last element of the list 2 with 2 3 4. Which has the net effect of adding to the list.

If your list is in a variable: (setq my-list '(1 2))

(setcdr my-list '(2 3 4)) ;=> '(2 3 4)

my-list ;=> '(1 2 3 4).

You could also just do a loop with add-to-list or cons. Note I'm assuming you don't care about order.

(dolist (thing things) (add-to-list 'list-var thing))

(dolist (thing things) (cons thing list-var))

I usually use the append as it is short to write and more direct. It also is probably among the fastest because it is written in C code whereas add-to-list is in elisp.

1
  • append is only efficient if all additional elements are already contained in a list and that list is added. It is inefficient for adding many elements sequentially or in small hunks at the tail since in this case its cost grows quadratically with the number of list entries. Note that I have confirmed that fact by timing.
    – Tobias
    Jan 10, 2018 at 16:54
3

The proper way to add many items to a list is consing in a loop.

If l is the list variable you add an item i by

(setq l (cons i l))

for which you can use the shorthand

(push i l)

The computational cost of this operation is independent of the length of the list l.

After collecting the items in this way they are in reverse order. If it is important to retain the order you can call

(setq l (nreverse l))

as the last action or just return (nreverse l). The cost of nreverse grows just linearly with the length of the list.

The costs of adding all elements with add-to-list grow quadratically with the length of the list (even without append set to t).

If you want to add an item to a list only if it is not already present. You have to search for that item. In that case you could consider using a hashmap (search the elisp info files for make-hash-table) or a sorted list to reduce the computational costs. Note that a single hash access is costly but the read/write access time is essentially independent on the number of elements in the hash.

3

I figured this question lacks an actual practical example that a person can copy-paste into their config, so here it is:

(defun merge-list-to-list (dst src)
  "Merges content of the 2nd list with the 1st one"
  (set dst
       (append (eval dst) src)))

It combines the non-destructive append function that merges two lists, and a destructive set.

Now, let's say your config had this code:

(add-to-list 'company-dabbrev-code-modes 'c++-mode)
(add-to-list 'company-dabbrev-code-modes 'c-mode)
(add-to-list 'company-dabbrev-code-modes 'php-mode)

(add-to-list 'auto-mode-alist '("\\.mzn\\'"      . minizinc-mode))
(add-to-list 'auto-mode-alist '("\\.glade$\\'"   . xml-mode))
(add-to-list 'auto-mode-alist '("\\PKGBUILD\\'"  . sh-mode))
(add-to-list 'auto-mode-alist '("\\.service\\'"  . conf-mode))
(add-to-list 'auto-mode-alist '("\\.m$"          . octave-mode))

You replace it with this:

(merge-list-to-list 'company-dabbrev-code-modes
                    '(c++-mode c-mode php-mode))

(merge-list-to-list 'auto-mode-alist
                    '(("\\.mzn\\'"      . minizinc-mode)
                      ("\\.glade$\\'"   . xml-mode)
                      ("\\PKGBUILD\\'"  . sh-mode)
                      ("\\.service\\'"  . conf-mode)
                      ("\\.m$"          . octave-mode)))
5
  • The purported analogy to add-to-list is misleading, as the most notable feature of add-to-list is that it doesn't add an item which already exists in the list. If you want append you should just use append, rather than wrapping that with a name that doesn't behave the way one would expect.
    – phils
    Jun 9, 2023 at 9:12
  • 1
    @phils append doesn't mutate original list though. Regarding the analogy: I honestly didn't even know add-to-list checks for duplicates. I would disagree calling it a "notable feature", because you would only know that from the docs or someone else. It is actually misleading, because the name of the function says nothing at all about removing dups. So in this case it's add-to-list that is misnamed and not the add-list-to-list. With that said, I can rename it if you think it would confuse old-timers. Perhaps, add-list-to-list-allow-dups…?
    – Hi-Angel
    Jun 9, 2023 at 12:53
  • "you would only know that from the docs" -- I don't think that's much of a problem unless you're in the habit of using unfamiliar elisp functions without even reading their docstrings (which I would consider fairly inexplicable). If I was to write such a function I would probably put append somewhere in the name.
    – phils
    Jun 9, 2023 at 13:08
  • @phils problem with append tho is this name does not imply the lists are merged. That is, upon executing (setq lhs '(1 2)) (append-list-to-list lhs '(3 4)) I would expect lhs to become (1 2 (3 4)) rather than (1 2 3 4). And yes, I don't necessarily read documentation if the name of the method is self-explanatory, such as print or while or goto-line. This ain't "inexplicable", in huge codebases you can't expect every function have docs, especially so given they have tendency to become outdated. The purpose of the function should be clear from its name and type-definition.
    – Hi-Angel
    Jun 10, 2023 at 2:49
  • So, I think merge-list-to-list might be a better naming, changed to it for now.
    – Hi-Angel
    Jun 10, 2023 at 3:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.