16

Q: how do I get the element number in a list?

nth gets element number n from a list:

(nth 2 '(a b c d))                      ; => c

I'd like to do the reverse: get the element number given the element:

(some-function 'c '(a b c d))           ; => 2

I may have missed it, but does such a function exist? How would one do this?

22
  1. Here's a function that is included with Emacs 24.3 and later:

    (cl-position 2 '(6 7 8 2 3 4)) ;; => 3
    

    (Before Emacs 24.3, use function position from library cl.el, which is included with Emacs.)

    You can use the :test keyword to specify the comparison function:

    (cl-position "bar" '("foo" "bar" "baz") :test 'equal) ;; => 1
    (cl-position '(1 2) '((3) (5 6) (1 2) nil) :test 'equal) ;; => 2
    

    Emacs Common Lisp Emulation Manual

  2. dash.el has a function that can do this: -elem-index

    (-elem-index 2 '(6 7 8 2 3 4)) ;; => 3
    (-elem-index "bar" '("foo" "bar" "baz")) ;; => 1
    (-elem-index '(1 2) '((3) (5 6) (1 2) nil)) ;; => 2
    

    It's not included with Emacs, but a lot of Emacs users already have it installed (it's a dependency of projectile, flycheck, and smartparens, which gives it a ton of coverage).

5

Well, if you want to roll your own instead of using cl-position, and you don't want to traverse twice (using length)...

(defun nth-elt (element xs)
  "Return zero-indexed position of ELEMENT in list XS, or nil if absent."
  (let ((idx  0))
    (catch 'nth-elt
      (dolist (x  xs)
        (when (equal element x) (throw 'nth-elt idx))
        (setq idx  (1+ idx)))
      nil)))

That's good for even old Emacs versions. However, it has this behavior difference, which you might or might not want: It works also for the cars of a dotted list. That is, it correctly returns the position instead of raising an error, for sexps such as (nth-elt 'c '(a b c . d)).

If you want to always raise an error for an improper list, then you will want to check for that case, which requires always traversing to the end of the list:

(defun nth-elt (element xs)
  "Return zero-indexed position of ELEMENT in list XS, or nil if absent."
  (let ((idx  0))
    (when (atom (cdr (last xs))) (error "Not a proper list"))
    (catch 'nth-elt
      (dolist (x  xs)
        (when (equal element x) (throw 'nth-elt idx))
        (setq idx  (1+ idx)))
      nil)))
2

Turns out it's a simple function to write, although it may not be all that efficient:

(defun nth-elt (elt list)
  "Return element number of ELT in LIST."
  (let ((loc (length (member elt list))))
    (unless (zerop loc)
      (- (length list) loc))))

(nth-elt 'c '(a b c d))                 ; => 2
(nth-elt 'f '(a b c d))                 ; => nil

I'd prefer a built-in solution if one exists, of course.

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.