>From bb262f675f22638d1523685c57bef130bd0cc74e Mon Sep 17 00:00:00 2001 From: Damien Cassou Date: Fri, 2 Sep 2022 14:54:36 +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. --- 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 1f6f80521c..019dc94919 100644 --- a/doc/lispref/sequences.texi +++ b/doc/lispref/sequences.texi @@ -880,6 +880,27 @@ Sequence Functions @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 edd4b01eab..13900f1663 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2743,6 +2743,11 @@ The default timeout value can be defined by the new variable ** New function 'seq-split'. This returns a list of sub-sequences of the specified sequence. ++++ +** New function 'seq-positions'. +This returns a list of the (zero-based) indices of elements matching a +given predicate in the specified sequence. + +++ ** 'plist-get', 'plist-put' and 'plist-member' are no longer limited to 'eq'. These function now take an optional comparison predicate argument. diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el index 1b4a49e4e3..8f08582fdd 100644 --- a/lisp/emacs-lisp/seq.el +++ b/lisp/emacs-lisp/seq.el @@ -445,6 +445,23 @@ seq-position (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 990dabe351..e19b8f9361 100644 --- a/lisp/emacs-lisp/shortdoc.el +++ b/lisp/emacs-lisp/shortdoc.el @@ -846,6 +846,10 @@ sequence :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 1a27467d29..d04974d002 100644 --- a/test/lisp/emacs-lisp/seq-tests.el +++ b/test/lisp/emacs-lisp/seq-tests.el @@ -482,6 +482,13 @@ test-seq-position (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.36.2