1

The built-in function add-to-ordered-list is for symbols, not strings. I have a list of strings and need to add another string to the list at a specific nth position. How can I accomplish this?

TASK:  Add "--cleared" as the nth 3 position of the ordered list; i.e., "--cleared" will become the 4th string in the list of strings. [I am using the nth N terminology to signify that the first position is nth 0 followed by nth 1 and so on.]

BEFORE:

'("%(binary)"
  "-f"
  "%(ledger-file)"
  "--empty"
  "--depth=3"
  "--no-total"
  "--amount"
  "'($1,000.00*a)/$1,000.00'"
  "--format"
  "hello-world"
  "bal")

AFTER:

'("%(binary)"
  "-f"
  "%(ledger-file)"
  "--cleared"
  "--empty"
  "--depth=3"
  "--no-total"
  "--amount"
  "'($1,000.00*a)/$1,000.00'"
  "--format"
  "hello-world"
  "bal")
3
  • 1
    FWIW, I just filed Emacs bug (enhancement request) #45539, asking for an optional arg to add-to-ordered-list for the :test predicate. If that existed you could just pass equal as its value to make the thing work for strings as well as symbols etc.
    – Drew
    Commented Dec 29, 2020 at 21:08
  • Apparently that bug will be fixed as suggested.
    – Drew
    Commented Dec 30, 2020 at 17:43
  • Note/FWIW: add-to-ordered-list is not just about adding an element at a given list position. It's apparently about having a list of unique elements (for some definition of unique, which currently is eq), and being able to not only add or remove but also change the position of an existing element. I've added this info to the description of bug 45539.
    – Drew
    Commented Dec 30, 2020 at 17:58

2 Answers 2

3

Here's one way to do this:

(let ((l (list "%(binary)"
               "-f"
               "%(ledger-file)"
               "--empty"
               "--depth=3"
               "--no-total"
               "--amount"
               "'($1,000.00*a)/$1,000.00'"
               "--format"
               "hello-world"
               "bal")))
  (push "--cleared" (nthcdr 3 l))
  l)
;; => ("%(binary)"
;;     "-f"
;;     "%(ledger-file)"
;;     "--cleared"
;;     "--empty"
;;     "--depth=3"
;;     "--no-total"
;;     "--amount"
;;     "'($1,000.00*a)/$1,000.00'"
;;     "--format"
;;     "hello-world"
;;     "bal")

This relies on the fact that nthcdr (and other similar functions) exist predefined as generalised variables; see (info "(elisp) Generalized Variables") and its subnodes.

N.B.: The above example constructs the list using the list function to ensure that it is safely mutable (since we subsequently want to modify it). Alternatively, you could pass a constant list literal to copy-sequence before modifying it. Modifying a quoted literal list would be wrong because such constants are part of the program and their mutations can persist over multiple evaluations of the same code. In other words, they result in undefined behaviour; see (info "(elisp) Mutability").

0

@Basil answered your question of how to get done what you want to do.

This "answer" is instead to confirm that you cannot use add-to-ordered-list to do what you request, and to say why that's the case.

You correctly said, "add-to-ordered-list is for symbols, not strings". More precisely, it's only for list elements that can be distinguished using eq (e.g., not equal or string=) -- that's the why.

The implementation of add-to-ordered-list uses a hash table to associate an "order" with some or all of the elements of a list. And the :test function for the hash table is eq. In addition, the function uses memq (which is also an eq test) to test for list membership. This use of eq is hard-coded.

I've filed Emacs bug #45539, to allow for other identity-distinguishing predicates besides eq. That would let you use add-to-ordered-list with strings, etc.


There's some confusion about what add-to-ordered-list does. It's not really about changing the positions of elements of ordinary lists or replacing an element at a particular position with some other value. It's about recording the order of elements in a list that's designed for that purpose: ordering them by some associated recorded number.

You can also have other elements in such a list, which have no recorded order. Such elements are placed at the end of the list, after any elements for which you've specified an order.

You can think of a specified order as a "score" of some kind. The list elements are placed in increasing numerical order of those scores, and any list elements that have no associated score are placed at the end of the list.

If you give multiple elements the same score (recorded "order") then they appear consecutively in the list; that's all. For them, you're in effect saying that the order among them in the list is not important, as they all have the same score.

You can remove the recorded score for a given element by calling add-to-ordered-list with it, and providing no ORDER argument (or a nil argument). That doesn't remove the element from the list; it just stops it from have a recorded (i.e., reserved) order, which moves it after any elements that do have recorded orders.

Here is another question (on StackOverflow) about add-to-ordered-list, which is similarly confused about it.

Hope this helps.

See also this StackOverflow question.

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.