Don't worry about alists having items with duplicate keys. Usually when you use alists, you access items with assoc
, which returns the first matching item in a list and ignores the rest. So the idiomatic way to treat alists is that every time you want to replace an item in alist with a new value for an old key, you just add a new dotted pair and ignore the old one. So no matter how many duplicates you have, assoc
will ignore them. You work via alist API and ignore the implementation details, so to speak. Alist's structure as a list is low-level and irrelevant.
Many useful Common Lisp functions are implemented through CL library, prefixed with cl-
. One such function is cl-remove-duplicates
, which is very versatile due to keyword arguments. (If you want a Common Lisp solution, just use remove-duplicates
). So if for some reason you want an alist with unique keys, remove all duplicate items but the newly added:
(cl-remove-duplicates my-list
:key #'car
:from-end t)
Providing car
as a key is equivalent to writing a custom test function that compares only cars of every 2 elements: :test (lambda (a b) (equal (car a) (car b))
. Why does it work? cl-remove-duplicates
already uses eql
as its test function and, as it says in the manual, a key function is like a filter through which the elements are seen by the function, so it doesn't compare the elements themselves, but first put elements through the key function.
You should use CL library every time it seems handy and elegant, because it's shipped with Emacs, but let's assume that you can't use it for some reason. Then there is dash.el
third-party list manipulation library. It has a function -distinct
, that removes multiple occurrences in a list. But it works with elements of a list verbatim, it doesn't understand alists! Or does it? You could supply a custom comparison function by assigning it to a -compare-fn
variable, and -distinct
will use it instead of bluntly comparing it with equal
. Just temporarily provide a function that compares by keys with let
:
(let ((-compare-fn (lambda (a b)
(equal (car a) (car b)))))
(-distinct my-list))