Elisp: How to delete an element from an association list with string key - emacs

Now this works just fine:
(setq al '((a . "1") (b . "2")))
(assq-delete-all 'a al)
But I'm using strings as keys in my app:
(setq al '(("a" . "foo") ("b" . "bar")))
And this fails to do anything:
(assq-delete-all "a" al)
I think that's because the string object instance is different (?)
So how should I delete an element with a string key from an association list? Or should I give up and use symbols as keys instead, and convert them to strings when needed?

If you know there can only be a single matching entry in your list, you can also use the following form:
(setq al (delq (assoc <string> al) al)
Notice that the setq (which was missing from your sample code) is very important for `delete' operations on lists, otherwise the operation fails when the deleted element happens to be the first on the list.

The q in assq traditionally means eq equality is used for the objects.
In other words, assq is an eq flavored assoc.
Strings don't follow eq equality. Two strings which are equivalent character sequences might not be eq. The assoc in Emacs Lisp uses equal equality which works with strings.
So what you need here is an assoc-delete-all for your equal-based association list, but that function doesn't exist.
All I can find when I search for assoc-delete-all is this mailing list thread:
http://lists.gnu.org/archive/html/emacs-devel/2005-07/msg00169.html
Roll your own. It's fairly trivial: you march down the list, and collect all those entries into a new list whose car does not match the given key under equal.
One useful thing to look at might be the Common Lisp compatibility library. http://www.gnu.org/software/emacs/manual/html_node/cl/index.html
There are some useful functions there, like remove*, with which you can delete from a list with a custom predicate function for testing the elements. With that you can do something like this:
;; remove "a" from al, using equal as the test, applied to the car of each element
(setq al (remove* "a" al :test 'equal :key 'car))
The destructive variant is delete*.

Emacs 27+ includes assoc-delete-all which will work for string keys, and can also be used with arbitrary test functions.
(assoc-delete-all KEY ALIST &optional TEST)
Delete from ALIST all elements whose car is KEY.
Compare keys with TEST. Defaults to ‘equal’.
Return the modified alist.
Elements of ALIST that are not conses are ignored.
e.g.:
(setf ALIST (assoc-delete-all KEY ALIST))
In earlier versions of Emacs, cl-delete provides an alternative:
(setf ALIST (cl-delete KEY ALIST :key #'car :test #'equal))
Which equivalently says to delete items from ALIST where the car of the list item is equal to KEY.
n.b. The answer by Kaz mentions this latter option already, but using the older (require 'cl) names of delete* and remove*, whereas you would now (for supporting Emacs 24+) use cl-delete or cl-remove (which are auto-loaded).

If using emacs 25 or newer you can use alist-get
(setf (alist-get "a" al t t 'equal) t)

Related

Presenting a list

I have this list of names and different languages
(setq l '((david spanish german)
(amanda italian spanish english)
(tom german french)))
I want to do the next with a function: for each language, I need every name relationed with every language.
For example, if I call the function with the list L:
(lenguages L)
I want to show this:
( (english (amanda))
(spanish (david amanda))
(italian (amanda))
(german(david tom))
(french(tom))
)
I have an idea of how to do this, but it shows just one item.
(defun lenguages(names)
(cond((null names) nil)
((list (cadar names) (list (caar names))))))
this last function only show (spanish (david))
An iteration-based task like this is best suited to Common Lisp's immensely powerful loop macro. You can read all the details about this macro in the GigaMonkeys book, but we'll just go over the parts you need for this problem here. Let's start with the function definition.
(defun lenguages (names)
...)
Inside this, we want to iterate over the provided list. We also want to collect some keys, so a hash table would be useful to have. Hash tables (called maps or dicts in many other languages) associate keys to values in a time-efficient way.
(loop with hash = (make-hash-table)
for entry in names
for name = (car entry)
do ...
finally ...)
The loop macro is very powerful and has a language all its own. The with clause declares a local variable, in this case a hash table. The first for defines an iteration variable. The loop will run with entry bound to each entry of names and will stop when it runs out of entries. The third line is another local variable, but unlike with, a for variable is rebound every time, so at each iteration name will be the first element of entry. The do block contains arbitrary Lisp code that will be executed each iteration, and finally contains a block of Lisp code to execute at the end of the loop.
Inside the do block, we want to add the person's name to the hash table entry for each language they know, so we need another loop to loop over the known languages.
(loop for lang in (cdr entry)
do (push name (gethash lang hash)))
This loop goes inside the do block of the outer one. For each language in the person's list of known languages, we want to prepend that person's name onto the hash value for that language. Normally, we would have to consider the case in which the hash key doesn't exist, but luckily for us Common Lisp defaults to nil if the hash key doesn't exist, and prepending an element to nil creates a one-element list, which is just what we want.
Now, when this loop is done, the hash table will contain all the languages and keys and lists of people who know them as values. This is the data that you want, but it's not in the format you want. In fact, if we put this in our finally block
(return hash)
We would get some semi-useful output* that tells us we're on the right track.
#S(HASH-TABLE :TEST FASTHASH-EQL ((TOM GERMAN FRENCH) . (TOM TOM))
((AMANDA ITALIAN SPANISH ENGLISH) . (AMANDA AMANDA AMANDA))
((DAVID SPANISH GERMAN) . (DAVID DAVID)))
Instead, let's do one more loop to convert this hash table to the list that you want it to be. Here's what we want in the finally block now.
(return (loop for key being the hash-keys of hash using (hash-value value)
collect (list key value)))
This uses the relatively obscure being syntax for the loop macro, which allows easy iteration over hash tables. You should read this as: for every key-value pair, collect a list containing the key followed by the value into a list, then return the accumulated list. This is yet another of the loop macros interesting features: it tries to provide primitives for common use cases such as accumulating values into a list. And it comes in handy in cases like this.
Here's the complete code block.
(defun lenguages (names)
(loop with hash = (make-hash-table)
for entry in names
for name = (car entry)
do (loop for lang in (cdr entry)
do (push name (gethash lang hash)))
finally (return (loop for key being the hash-keys of hash using (hash-value value)
collect (list key value)))))
That link I provided earlier is to the GigaMonkeys book on Common Lisp, which is available online for free. I strongly encourage reading through it, as it's an amazing reference for all things Common Lisp. Especially if you're just starting out, that book can really set you in the right direction.
* Your output format may vary on this. The implementation chooses how to output structs.
The other answer is fine: here is a version which does not use loop or intermediate hashtables, but instead builds the required association-list directly. It's worth comparing the efficiency of this against the hash-table-based one: it does a lot more searching down lists, but in practice, for small amounts of data such things are often faster (hashtables have nontrivial overhead in many implementations) and it will always use less storage as it builds no structure it does not return.
Note that:
this will return results in a different order in general (it has no dependency on hash ordering);
this will ensure there is only one occurrence of each person for each language: (languages '((david german german))) is ((german (david))) not ((german (david david))) -- it pays some performance cost (which could be ameliorated for large data using more hashtables) for doing this.
So, here it is:
(defun languages (people)
(let ((langs '())) ;the map we are building
(dolist (pl people langs)
(destructuring-bind (person . person-languages) pl
(dolist (lang person-languages)
(let ((entry (assoc lang langs)))
(if (not (null entry))
;; there's an entry for lang: add the person to it
(pushnew person (second entry))
;; there is no entry, create one with person in it
(setf langs `((,lang (,person)) ,#langs)))))))))
(Note also that the loop based version could use loop's destructuring, which might be a little clearer.)

How to return the value instead of key by completing-read

(completing-read
"Complete a foo: "
'(("foobar1" "~/foobar1/") ("barfoo" "/usr/barfoo/") ("foobaz" "/hello/")))
As shown above, I would like to prompt for "foobar1","barfoo", and "foobaz" but getting the paired directory in return.
Moverover, if I have a hash-table like this
(cl-defstruct person ID name)
(setq person-object (make-person :ID 123 :name "foo"))
(setq person-table (make-hash-table))
(pushash (person-ID person-object) person-object person-table)
How can I prompt for the person name but getting a person ID in return?
There's no way to get completing-read to return the value instead of the key, so you have to do the lookup yourself:
(let ((completions '(("foobar1" "~/foobar1/") ("barfoo" "/usr/barfoo/") ("foobaz" "/hello/"))))
(cadr (assoc (completing-read "Complete a foo: " completions) completions)))
As for the hash table, since the name is not the key, you need to iterate through every object in the hash table to find it, using maphash. Since it would be wasteful to keep iterating after you've found what you're looking for, you could use catch and throw, like this:
(catch 'found-it
(maphash
(lambda (key value)
(when (equal (person-name value) desired-name)
(throw 'found-it key)))
person-table))
This will return the person ID, or nil if there's no person whose name equals desired-name.
#legoscia provided a good answer: completing-read does not give you access to the value associated with a key that it uses for completion. E.g., for an alist COLLECTION argument, it does not give you access to the cdr of a chosen alist key.
For an alist you can use assoc to get the first matching alist element, and for a hash table you can maphash or do a get.
But these approaches preclude getting the particular value associated with a particular chosen key occurrence when there are duplicates of the key, that is, when multiple candidates have the same key, or name.
You cannot get the 2nd matching element, or the 13th. In fact, vanilla Emacs completing-read eliminates duplication of completion candidates that have the same key (name). For vanilla Emacs, any information in the cdr of an alist entry is wasted. You can use an alist, for convenience, if you already have one, but if not then you might as well just use a list of names (strings or symbols), not conses.
If you use Icicles then alist entries are not wasted. There is no problem retrieving cdr values. You can easily get to the complete information of a candidate you choose, after completing-read is done.
Icicles does this by using propertized strings as candidates, and by enhancing completing-read so that it can return the complete string, properties and all, that a user chooses. You can recuperate the complete alist entry from the propertized string that is returned.
When is it important to be able to have and use duplicate keys that can have different associated values? And how can a user tell them apart (e.g., in *Completions*), if they are duplicates?
Examples:
Bookmarks that have the same name but are for different targets - for example, for different files with the same relative name in different directories.
Lines or other text in a buffer that match a pattern or that contain a marker. This includes matches in Icicles search, where you can define search contexts any way you like (not just lines). It also includes buffer zones (including restrictions, aka narrowings) and buffer positions (markers).
Candidates that have the same text but whose annotations are different. (User input is not matched against annotations shown in *Completions*.)
Imenu items that have the same name, e.g., multiple definitions of objects (e.g. functions) that have the same name.
Tagged items (e.g. functions) that have the same name.
Candidates that are other Lisp objects, such as frames, that can have the same name.
In Icicles, how does a user choose one among several completion candidates that have the same name?
Users can control the order (sorting) of the candidates, including changing order on the fly. *Completions* shows them to you in a specific order. You can cycle among candidates or choose any of them directly. You are not limited to matching, to choose. (With duplicate candidates, matching can be insufficient to get you to only one of them.)
*Completions* can also show you additional information about candidates, which distinguishes them even when they have the same name/text. Such info could be surrounding text (if the candidates are matching buffer text), or candidate metadata (e.g. file or bookmark attributes).
You can also see important additional information about the current candidate (e.g. during cycling) in the mode line.
You can get additional information (complete *Help*) about the current candidate on demand, by hitting a key.
What do you have to do, to be able to take advantage of this Icicles feature in your own code?
See Defining Tripping Commands for how to define your own commands that let users trip among (explore) candidates might have associated positional or other navigational information. (See Tripping for predefined Icicles tripping commands.)
A brief overview of what to do in your command:
Bind variable icicle-whole-candidate-as-text-prop-p to non-nil.
Set variable icicle-candidates-alist to the alist that you pass to completing-read. This has the effect of encoding, as a text property on the candidate display string, the entire corresponding original alist entry.
Use icicle-get-alist-candidate after calling completing-read, to recover that complete information about the candidate the user chooses, that is, the complete alist element, kncluding the cdr.
(See also: Note to programmers using Icicles.)
http://www.howardism.org/Technical/Emacs/alt-completing-read.html
You have been looking for alt-completing-read !
Simpler version
(let ((choices '(("First" . 'first-choice)
("Second" . 'second-choice)
("Third" . 'third-choice))))
(alist-get
(completing-read "Choose: " choices)
choices nil nil 'equal))
Complex version
(defun alt-completing-read (prompt collection &optional predicate require-match initial-input hist def inherit-input-method)
"Calls `completing-read' but returns the value from COLLECTION.
Simple wrapper around the `completing-read' function that assumes
the collection is either an alist, or a hash-table, and returns
the _value_ of the choice, not the selected choice. For instance,
give a variable of choices like:
(defvar favorite-hosts '((\"Glamdring\" . \"192.168.5.12\")
(\"Orcrist\" . \"192.168.5.10\")
(\"Sting\" . \"192.168.5.220\")
(\"Gungnir\" . \"192.168.5.25\")))
We can use this function to `interactive' without needing to call
`alist-get' afterwards:
(defun favorite-ssh (hostname)
\"Start a SSH session to a given HOSTNAME.\"
(interactive (list (alt-completing-read \"Host: \" favorite-hosts)))
(message \"Rockin' and rollin' to %s\" hostname))"
;; Yes, Emacs really should have an `alistp' predicate to make this code more readable:
(cl-flet ((assoc-list-p (obj) (and (listp obj) (consp (car obj)))))
(let* ((choice
(completing-read prompt collection predicate require-match initial-input hist def inherit-input-method))
(results (cond
((hash-table-p collection) (gethash choice collection))
((assoc-list-p collection) (alist-get choice collection def nil 'equal))
(t choice))))
(if (listp results) (first results) results))))

Merging symbols in common lisp

I want to insert a char into a list. However, I want to merge this char with the last symbol in the list. With appends and cons the result is always two different symbols. Well, I want one merged symbol to be my result.
Example:
(XXXX 'a '5) ====> (a5)
What I would like to have, instead of:
(XXXX 'a '5) ====> (a 5)
You cannot "merge symbols" in Lisp.
First of all, 5 is not a symbol, but a number. If you want a symbol named "5" you have to type it as |5| (for example).
If a function takes the symbol A and symbol |5|, and produces the symbol A5, it has not merged symbols. It has created a new symbol whose name is the catenation of the names of those input symbols.
Properly designed Lisp programs rarely depend on how a symbol is named. They depend on symbols being unique entities.
If you're using symbols to identify things, and both 5 and A identify some entity, the best answer isn't necessarily to create a new symbol which is, in name at least, is a mashup of these two symbols. For instance, a better design might be to accept that names are multi-faceted or compound in some way. Perhaps the list (A 5) can serve as a name.
Common Lisp functions themselves can have compound names. For instance (setf foo) is a function name. Aggregates like lists can be names.
If you simply need the machine to invent unique symbols at run-time, consider using the gensym function. You can pass your own prefix to it:
(gensym "FOO") -> #:FOO0042
Of course, the prefix can be the name of some existing symbol, pulled out via symbol-name. The symbol #:FOO0042 is not unique because of the 0042 but because it is a freshly allocated object in the address space. The #: means it is not interned in any package. The name of the symbol is FOO0042.
If you still really want to, a simple way to take the printed representation of a bunch of input objects and turn it into a symbol is this:
(defun mashup-symbol (&rest objects)
(intern (format nil "~{~a~}" objects)))
Examples:
(mashup-symbol 1 2 3) -> |123|
(mashup-symbol '(a b) 'c 3) -> |(A B)C3|
Define this:
(defun symbol-append (&rest symbols)
(intern (apply #'concatenate 'string
(mapcar #'symbol-name symbols))))
and then use it as:
* (symbol-append 'a 'b 'c)
ABC
* (apply #'symbol-append '(a b c))
ABC
If you expect your arguments to contain stuff besides symbols, then replace symbol-name with:
(lambda (x)
(typecase x ...))
or a pre-existing CL function (that I've forgotten :() that stringifies anything.
The answer to the question you ask is
(defun concatenate-objects-to-symbol (&rest objects)
(intern (apply #'concatenate 'string (mapcar #'princ-to-string objects))))
(concatenate-objects 'a 'b) ==> ab
Oh, if you want a list as the result:
(defun xxxx (s1 s2) (list (concatenate-objects-to-symbol s1 s2)))
However, I am pretty sure this is not the question you actually want to ask.
Creating new symbols programmatically is not something beginners should be doing...

How to acquire unique object id in Emacs Lisp?

Does emacs lisp have a function that provides a unique object identifier, such as e.g. a memory address? Python has id(), which returns an integer guaranteed to be unique among presently existing objects. What about elisp?
The only reason I know for wanting a function like id() is to compare objects, and ensure that they only compare equal if they are the same (as in, in the same memory location). In Lisps, this is done a bit differently from in Python:
In most lisps, including elisp, there are several different notions of equality. The most expensive, and weakest equivalence is equal. This is not what you want, since two lists (say) are equal if they have the same elements (tested recursively with equal). As such
(equal (list 1 2) (list 1 2)) => T
is true. At the other end of the spectrum is eq, which tests "identity" rather than equality:
(eq (list 1 2) (list 1 2)) => NIL
This is what you want, I think.
So, it seems that Python works by providing one equality test, and then a function that gives you a memory location for each object, which then can be compared as integers. In Elisp (and at least Common Lisp too), on the other hand, there is more than one meaning of "equality".
Note, there is also "eql", which lies somewhere between the two.
(EDIT: My original answer probably wasn't clear enough about why the distinction between eq and equal probably solves the problem the original poster was having)
There is no such feature in Emacs Lisp, as far as I know. If you only need equality, use eq, which performs a pointer comparison behind the scenes.
If you need a printable unique identifier, use gensym from the cl package.
If you need a unique identifier to serve as an index in a data structure, use gensym (or maintain your own unique id — gensym is simpler and less error-prone).
Some languages bake a unique id into every object, but this has a cost: either every object needs extra memory to store the id, or the id is derived from the address of the object, which precludes modifying the address. Python chooses to pay the cost, Emacs chooses not to.
My whole point in asking the question was that I was looking for a way to distinguish between the printed representations of different symbols that have the same name. Thanks to the elisp manual, I've discovered the variable print-gensym, which, when non-nil, causes #: to be prepended to uninterned symbols printed. Moreover, if the same call to print prints the same uninterned symbol more than once, it will mark the first one with #N= and subsequent ones with `#N#. This is exactly the kind of functionality I was looking for. For example:
(setq print-gensym t)
==> t
(make-symbol "foo")
==> #:foo
(setq a (make-symbol "foo"))
==> #:foo
(cons a a)
==> (#1=#:foo . #1#)
(setq b (make-symbol "foo"))
==> #:foo
(cons a b)
==> (#:foo . #:foo)
The #: notation works for read as well:
(setq a '#:foo)
==> #:foo
(symbol-name a)
==> "foo"
Note the ' on '#:foo--the #: notation is a symbol-literal. Without the ', the uninterned symbol is evaluated:
(symbol-name '#:foo)
==> "foo"
(symbol-name #:foo)
==> (void-variable #:foo)

Emacs Lisp: how to avoid inserting a duplicate list item?

How do I check if a string is already in a list in Emacs Lisp? I need to check if a certain path string is already in exec-path, and then add it to that list if it's not.
The function add-to-list will check automatically before adding
(setq a '(1 2 3))
(add-to-list 'a 4)
(add-to-list 'a 3)
will result in a equal to (4 1 2 3)
From Emacs 26 C-h f add-to-list:
(add-to-list LIST-VAR ELEMENT &optional APPEND COMPARE-FN)
Add ELEMENT to the value of LIST-VAR if it isn’t there yet.
The test for presence of ELEMENT is done with ‘equal’, or with
COMPARE-FN if that’s non-nil.
If ELEMENT is added, it is added at the beginning of the list,
unless the optional argument APPEND is non-nil, in which case
ELEMENT is added at the end.
The return value is the new value of LIST-VAR.
This is handy to add some elements to configuration variables,
but please do not abuse it in Elisp code, where you are usually
better off using ‘push’ or ‘cl-pushnew’.
If you want to use ‘add-to-list’ on a variable that is not
defined until a certain package is loaded, you should put the
call to ‘add-to-list’ into a hook function that will be run only
after loading the package. ‘eval-after-load’ provides one way to
do this. In some cases other hooks, such as major mode hooks,
can do the job.
In addition to cobbal's answer about add-to-list, there's also cl-pushnew:
(require 'cl-lib)
(setq list-of-strings '("one" "two" "three"))
(cl-pushnew "two" list-of-strings :test #'string=)
⇒ ("one" "two" "three")
(cl-pushnew "zero" list-of-strings :test #'string=)
⇒ ("zero" "one" "two" "three")
The :test #'string= argument is needed in your case because, cl-pushnew uses eql to compare by default, and it doesn't treat two strings with the same content as equal. (equal) would work too.
(eql "some-string" "some-string")
⇒ nil
(string= "some-string" "some-string")
⇒ t
(equal "some-string" "some-string")
⇒ t
From Emacs 26 C-h f cl-pushnew:
cl-pushnew is a Lisp macro in ‘cl-lib.el’.
(cl-pushnew X PLACE [KEYWORD VALUE]...)
(cl-pushnew X PLACE): insert X at the head of the list if not already there.
Like (push X PLACE), except that the list is unmodified if X is ‘eql’ to
an element already on the list.
Keywords supported: :test :test-not :key