From 4751b51d5e1182975aa002af08a625e4859ec276 Mon Sep 17 00:00:00 2001 From: Damien Cassou Date: Sun, 4 Sep 2022 13:21:59 +0200 Subject: [PATCH] Add new function `seq-positions' * doc/lispref/sequences.texi (Sequence Functions): Document it. * lisp/emacs-lisp/seq.el (seq-positions): New function. * lisp/emacs-lisp/shortdoc.el (sequence): Mention it. * test/lisp/emacs-lisp/seq-tests.el (test-seq-positions): Test it (bug#57548). --- doc/lispref/sequences.texi | 21 +++++++++++++++++++++ etc/NEWS | 5 +++++ lisp/emacs-lisp/seq.el | 17 +++++++++++++++++ lisp/emacs-lisp/shortdoc.el | 4 ++++ test/lisp/emacs-lisp/seq-tests.el | 7 +++++++ 5 files changed, 54 insertions(+) diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi index 2ee19efb1a9..214b1e76e15 100644 --- a/doc/lispref/sequences.texi +++ b/doc/lispref/sequences.texi @@ -898,6 +898,27 @@ use instead of the default @code{equal}. @end example @end defun +@defun seq-positions sequence elt &optional testfn + This function returns a list of the (zero-based) indices of the +elements in @var{sequence} for which @var{testfn} returns +non-@code{nil} when passed the element and @var{elt} as +arguments. @var{testfn} defaults to @code{equal}. + +@example +@group +(seq-positions '(a b c a d) 'a) +@result{} (0 3) +@end group +@group +(seq-positions '(a b c a d) 'z) +@result{} nil +@end group +@group +(seq-positions '(11 5 7 12 9 15) 10 #'>=) +@result{} (0 3 5) +@end group +@end example +@end defun @defun seq-uniq sequence &optional function This function returns a list of the elements of @var{sequence} with diff --git a/etc/NEWS b/etc/NEWS index ee450317a0c..6c0cf19fe6b 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2742,6 +2742,11 @@ compiler now emits a warning about this deprecated usage. These can be used for buttons in buffers and the like. See the "(elisp) Icons" and "(emacs) Icons" nodes in the manuals for details. ++++ +** New function 'seq-positions'. +This returns a list of the (zero-based) indices of elements matching a +given predicate in the specified sequence. + +++ ** New arguments MESSAGE and TIMEOUT of 'set-transient-map'. MESSAGE specifies a message to display after activating the transient diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el index 64197b55e5f..31dcfa98b40 100644 --- a/lisp/emacs-lisp/seq.el +++ b/lisp/emacs-lisp/seq.el @@ -459,6 +459,23 @@ Equality is defined by the function TESTFN, which defaults to `equal'." (setq index (1+ index))) nil))) +;;;###autoload +(cl-defgeneric seq-positions (sequence elt &optional testfn) + "Return indices for which (TESTFN (seq-elt SEQUENCE index) ELT) is non-nil. + +TESTFN is a two-argument function which is passed each element of +SEQUENCE as first argument and ELT as second. TESTFN defaults to +`equal'. + +The result is a list of (zero-based) indices." + (let ((result '())) + (seq-do-indexed + (lambda (e index) + (when (funcall (or testfn #'equal) e elt) + (push index result))) + sequence) + (nreverse result))) + ;;;###autoload (cl-defgeneric seq-uniq (sequence &optional testfn) "Return a list of the elements of SEQUENCE with duplicates removed. diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el index 6a366ec0fc0..2472479bad6 100644 --- a/lisp/emacs-lisp/shortdoc.el +++ b/lisp/emacs-lisp/shortdoc.el @@ -846,6 +846,10 @@ A FUNC form can have any number of `:no-eval' (or `:no-value'), :eval (seq-find #'numberp '(a b 3 4 f 6))) (seq-position :eval (seq-position '(a b c) 'c)) + (seq-positions + :eval (seq-positions '(a b c a d) 'a) + :eval (seq-positions '(a b c a d) 'z) + :eval (seq-positions '(11 5 7 12 9 15) 10 #'>=)) (seq-length :eval (seq-length "abcde")) (seq-max diff --git a/test/lisp/emacs-lisp/seq-tests.el b/test/lisp/emacs-lisp/seq-tests.el index 6249e486173..d95b35c45eb 100644 --- a/test/lisp/emacs-lisp/seq-tests.el +++ b/test/lisp/emacs-lisp/seq-tests.el @@ -490,6 +490,13 @@ Evaluate BODY for each created sequence. (should (= (seq-position seq 'a #'eq) 0)) (should (null (seq-position seq (make-symbol "a") #'eq))))) +(ert-deftest test-seq-positions () + (with-test-sequences (seq '(1 2 3 1 4)) + (should (equal '(0 3) (seq-positions seq 1))) + (should (seq-empty-p (seq-positions seq 9)))) + (with-test-sequences (seq '(11 5 7 12 9 15)) + (should (equal '(0 3 5) (seq-positions seq 10 #'>=))))) + (ert-deftest test-seq-sort-by () (let ((seq ["x" "xx" "xxx"])) (should (equal (seq-sort-by #'seq-length #'> seq) -- 2.39.2