1

Given '(a b c d), how would I remove b and c to end up with '(a d)? Is there a "right" way to remove multiple, distinct elements from a list?

Obviously, one could do (delete 'c (delete 'b '(a b c d))). But this doesn't generalize or scale well1.

Other possibilities include:

  • Create a predicate function and map it over the list with something like delete-if-not.

  • Perform a set difference (if that's even different from the list mapping)

I am getting lost in all the different implementations which accomplish, so it seems, the same thing (i.e. delete, dash, seq, cl-delete, etc.).

I would prefer to only use whatever ships with vanilla Emacs (26.3), unless there is a compelling reason to introduce an external dependency.


1 I understand the distinction between remove and delete in that one preserves the original list while the other does not. It's my impression that the two are more or less interchangeable otherwise.

  • Would it not be more generic to ask how to delete elements from one list from another list rather than using "b" and "c"? I think that's what you intend to convey. – RichieHH Feb 2 at 20:24
  • "this doesn't generalize or scale well" -- when you talk about scaling, are you significantly concerned about performance? For smallish lists all the typical approaches will be perfectly serviceable, but you might see dramatic performance benefits when processing large lists if you used hashing to reduce the algorithmic complexity. irreal.org/blog/?p=8621 demonstrates this for a very similar problem. – phils Feb 3 at 3:49
  • @phils by generalize I meant delete doesn't accept a list and by scale I meant that to delete more items, it would require a process of (delete (delete (delete ...). I wasn't concerning myself with performance yet. However, your reference is noted and much appreciated! – Lorem Ipsum Feb 3 at 22:06
  • 2
    Note that you should never pass a quoted constant list, such as '(a b c d), directly to a destructive function such as delete, as the constant will then be modified globally and the same code may behave differently each time it's run. Instead, either make a copy of the constant list first, e.g. (copy-sequence '(a b c d)), create a new list each time, e.g. (list 'a 'b 'c 'd), or pass the constant to non-destructive functions such as remove. – Basil Feb 4 at 10:39
3

What about seq-remove from seq.el, which shipped with Emacs 25?

(require 'seq)

(let ((l '(a b c d)))
  (seq-remove (lambda (x) (memq x '(b c)))
              l))  ; (a d)

Or, if you just have a list of elements to exclude, seq-difference from the same package?

(let ((l '(a b c d)))
  (seq-difference l '(b c)))  ; (a d)

See (info "(elisp) Sequence Functions") for more information.

| improve this answer | |
2

You can also use cl-remove-if or cl-delete-if, with a predicate such as

(lambda (x) (memq x '(b c))

Or whatever other test you want (e.g. member). There are also functions cl-remove-if-not and cl-delete-if-not.

| improve this answer | |
2

See cl-set difference.

(cl-set-difference '(a b c d e f) '(b d))

It might help.

https://www.gnu.org/software/emacs/manual/html_node/cl/Lists-as-Sets.html

| improve this answer | |
0

We can reduce over the list of elements you want to remove, and each time through, remove that element from the seq.

ELISP> (cl-reduce (lambda (seq elt) (delete elt seq))
                  '(b c)
                  :initial-value '(a b c d))
(a d)

Using a helper function from dash, which reduces the code somewhat:

ELISP> (cl-reduce (-flip #'delete)
                  '(b c)
                  :initial-value '(a b c d))
(a d)
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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