unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#57548: Add new function `seq-positions'
@ 2022-09-02 18:43 Damien Cassou
  2022-09-02 19:00 ` Eli Zaretskii
  2022-09-03  1:42 ` Michael Heerdegen
  0 siblings, 2 replies; 12+ messages in thread
From: Damien Cassou @ 2022-09-02 18:43 UTC (permalink / raw)
  To: 57548

[-- Attachment #1: Type: text/plain, Size: 192 bytes --]

Tags: patch

Hi,

here is a patch adding seq-positions to seq.el.

-- 
Damien Cassou

"Success is the ability to go from one failure to another without
losing enthusiasm." --Winston Churchill

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-new-function-seq-positions.patch --]
[-- Type: text/patch, Size: 4041 bytes --]

From cdb5806faa1ce7c5a6c4bd11ff489e392d6e7fe6 Mon Sep 17 00:00:00 2001
From: Damien Cassou <damien@cassou.me>
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        | 17 +++++++++++++++++
 etc/NEWS                          |  5 +++++
 lisp/emacs-lisp/seq.el            | 12 ++++++++++++
 lisp/emacs-lisp/shortdoc.el       |  3 +++
 test/lisp/emacs-lisp/seq-tests.el |  5 +++++
 5 files changed, 42 insertions(+)

diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi
index 1f6f80521c..dc13180a11 100644
--- a/doc/lispref/sequences.texi
+++ b/doc/lispref/sequences.texi
@@ -880,6 +880,23 @@ Sequence Functions
 @end example
 @end defun
 
+@defun seq-positions sequence elt &optional testfn
+  This function returns a list of the positions of the elements in
+@var{sequence} that are equal to @var{elt}.  If the optional argument
+@var{testfn} is non-@code{nil}, it is a function of two arguments to
+use instead of the default @code{equal}.
+
+@example
+@group
+(seq-positions '(a b c a d) 'a)
+@result{} (0 3)
+@end group
+@group
+(seq-position '(a b c a d) 'z)
+@result{} nil
+@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 1512d45fdc..0f09891914 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -2710,6 +2710,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 positions of a given element 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 b6f0f66e5b..7db7cd0cd6 100644
--- a/lisp/emacs-lisp/seq.el
+++ b/lisp/emacs-lisp/seq.el
@@ -445,6 +445,18 @@ seq-position
         (setq index (1+ index)))
       nil)))
 
+;;;###autoload
+(cl-defgeneric seq-positions (sequence elt &optional testfn)
+  "Return a list of the positions of ELT in SEQ.
+Equality is defined by TESTFN if non-nil or by `equal' if nil."
+  (let ((result '())
+        (index 0))
+    (seq-doseq (e sequence)
+      (when (funcall (or testfn #'equal) e elt)
+        (push index result))
+      (setq index (1+ index)))
+    (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..80b9c0a69e 100644
--- a/lisp/emacs-lisp/shortdoc.el
+++ b/lisp/emacs-lisp/shortdoc.el
@@ -846,6 +846,9 @@ 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))
   (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..7f06a7e618 100644
--- a/test/lisp/emacs-lisp/seq-tests.el
+++ b/test/lisp/emacs-lisp/seq-tests.el
@@ -482,6 +482,11 @@ 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)))))
+
 (ert-deftest test-seq-sort-by ()
   (let ((seq ["x" "xx" "xxx"]))
     (should (equal (seq-sort-by #'seq-length #'> seq)
-- 
2.36.2


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* bug#57548: Add new function `seq-positions'
  2022-09-02 18:43 bug#57548: Add new function `seq-positions' Damien Cassou
@ 2022-09-02 19:00 ` Eli Zaretskii
  2022-09-03  8:01   ` Damien Cassou
  2022-09-03  1:42 ` Michael Heerdegen
  1 sibling, 1 reply; 12+ messages in thread
From: Eli Zaretskii @ 2022-09-02 19:00 UTC (permalink / raw)
  To: Damien Cassou; +Cc: 57548

> From: Damien Cassou <damien@cassou.me>
> Date: Fri, 02 Sep 2022 20:43:24 +0200
> 
> +@defun seq-positions sequence elt &optional testfn
> +  This function returns a list of the positions of the elements in
> +@var{sequence} that are equal to @var{elt}.

We use "index", not "position".  In any case, the documentation should
explain what you mean by that, and it should definitely say that the
index/position are zero-based.

> +@example
> +@group
> +(seq-positions '(a b c a d) 'a)
> +@result{} (0 3)
> +@end group
> +@group
> +(seq-position '(a b c a d) 'z)
> +@result{} nil

seq-position or seq-positions?

> +(cl-defgeneric seq-positions (sequence elt &optional testfn)
> +  "Return a list of the positions of ELT in SEQ.
> +Equality is defined by TESTFN if non-nil or by `equal' if nil."

Our style is to say

  Equality is defined by the function TESTFN, which defaults to `equal'.

> --- a/test/lisp/emacs-lisp/seq-tests.el
> +++ b/test/lisp/emacs-lisp/seq-tests.el
> @@ -482,6 +482,11 @@ 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)))))

Should we test more than just one type of sequences?

Thanks.





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#57548: Add new function `seq-positions'
  2022-09-02 18:43 bug#57548: Add new function `seq-positions' Damien Cassou
  2022-09-02 19:00 ` Eli Zaretskii
@ 2022-09-03  1:42 ` Michael Heerdegen
  2022-09-03  8:02   ` Damien Cassou
  1 sibling, 1 reply; 12+ messages in thread
From: Michael Heerdegen @ 2022-09-03  1:42 UTC (permalink / raw)
  To: Damien Cassou; +Cc: 57548

Damien Cassou <damien@cassou.me> writes:

> here is a patch adding seq-positions to seq.el.

+@defun seq-positions sequence elt &optional testfn
+  This function returns a list of the positions of the elements in
+@var{sequence} that are equal to @var{elt}.  If the optional argument
+@var{testfn} is non-@code{nil}, it is a function of two arguments to
+use instead of the default @code{equal}.

We do not need to limit this to equivalence relations.  A TESTFUN of, say,
(apply-partially #'<= 10) could be similarly useful.

+;;;###autoload
+(cl-defgeneric seq-positions (sequence elt &optional testfn)
+  "Return a list of the positions of ELT in SEQ.
+Equality is defined by TESTFN if non-nil or by `equal' if nil."
+  (let ((result '())
+        (index 0))
+    (seq-doseq (e sequence)
+      (when (funcall (or testfn #'equal) e elt)
+        (push index result))
+      (setq index (1+ index)))
+    (nreverse result)))

Could this maybe (simpler) call `seq-do-indexed'?


TIA,

Michael.





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#57548: Add new function `seq-positions'
  2022-09-02 19:00 ` Eli Zaretskii
@ 2022-09-03  8:01   ` Damien Cassou
  2022-09-03 10:16     ` Eli Zaretskii
  2022-09-04  2:27     ` Michael Heerdegen
  0 siblings, 2 replies; 12+ messages in thread
From: Damien Cassou @ 2022-09-03  8:01 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 57548

[-- Attachment #1: Type: text/plain, Size: 2377 bytes --]

Eli Zaretskii <eliz@gnu.org> writes:
> We use "index", not "position".

I changed the text in the manual, NEWS and docstring to talk about
"index" instead of "position".  I kept the word "positions" in the
function name because there is already a `seq-position` function and the
2 are so similar that I think they deserve a similar name. What do you
think?


> In any case, the documentation should explain what you mean by that,


I haven't found another such explanation in seq.texi so I'm not sure
what you means.  I would be happy to write something if you feel
something is still missing though.


> and it should definitely say that the index/position are zero-based.


done in the manual, NEWS and docstring.


>> +@group
>> +(seq-position '(a b c a d) 'z)
>> +@result{} nil
>
> seq-position or seq-positions?


you are so right.  Thank you for the careful review.


> Our style is to say
>   Equality is defined by the function TESTFN, which defaults to `equal'.


fixed.  If you want, I can prepare another patch to apply the same
change to the docstring of the already existing `seq-position`: it
contains the same phrasing.


>> +(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)))))
>
> Should we test more than just one type of sequences?


The `with-test-sequences` call checks 3 types of sequences already as
far as I understand. Do you mean something else?


Michael Heerdegen <michael_heerdegen@web.de> writes:
> We do not need to limit this to equivalence relations.  A TESTFUN of, say,
> (apply-partially #'<= 10) could be similarly useful.


Indeed, such a function would certainly make sense.  I would refrain
from calling it `seq-positions` though because there is already a
`seq-position` function that is very similar.  I guess the function you
suggest wouldn't take an element as argument either.  In any case, I
think the current function makes sense on its own and what you are
asking for is a new function that seems out of scope of this current
patch.  If you give me a name (what about `(seq-matching-positions seq
pred)`?), I volunteer to send a new patch in a new mail thread.

Best

-- 
Damien Cassou

"Success is the ability to go from one failure to another without
losing enthusiasm." --Winston Churchill

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-new-function-seq-positions.patch --]
[-- Type: text/x-patch, Size: 4069 bytes --]

From e3c696f73b37da24eb3883d365a72f014a3e52ee Mon Sep 17 00:00:00 2001
From: Damien Cassou <damien@cassou.me>
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        | 17 +++++++++++++++++
 etc/NEWS                          |  5 +++++
 lisp/emacs-lisp/seq.el            | 13 +++++++++++++
 lisp/emacs-lisp/shortdoc.el       |  3 +++
 test/lisp/emacs-lisp/seq-tests.el |  5 +++++
 5 files changed, 43 insertions(+)

diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi
index 1f6f80521c..6b198ba731 100644
--- a/doc/lispref/sequences.texi
+++ b/doc/lispref/sequences.texi
@@ -880,6 +880,23 @@ 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} that are equal to @var{elt}.  If the
+optional argument @var{testfn} is non-@code{nil}, it is a function of
+two arguments to use instead of the default @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
+@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 1512d45fdc..a8fc8dcc34 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -2710,6 +2710,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 a given element 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 b6f0f66e5b..7e8576fffb 100644
--- a/lisp/emacs-lisp/seq.el
+++ b/lisp/emacs-lisp/seq.el
@@ -445,6 +445,19 @@ seq-position
         (setq index (1+ index)))
       nil)))
 
+;;;###autoload
+(cl-defgeneric seq-positions (sequence elt &optional testfn)
+  "Return a list of the (zero-based) indices of ELT in SEQ.
+Equality is defined by the function TESTFN, which defaults to
+`equal'."
+  (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..80b9c0a69e 100644
--- a/lisp/emacs-lisp/shortdoc.el
+++ b/lisp/emacs-lisp/shortdoc.el
@@ -846,6 +846,9 @@ 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))
   (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..7f06a7e618 100644
--- a/test/lisp/emacs-lisp/seq-tests.el
+++ b/test/lisp/emacs-lisp/seq-tests.el
@@ -482,6 +482,11 @@ 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)))))
+
 (ert-deftest test-seq-sort-by ()
   (let ((seq ["x" "xx" "xxx"]))
     (should (equal (seq-sort-by #'seq-length #'> seq)
-- 
2.36.2


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* bug#57548: Add new function `seq-positions'
  2022-09-03  1:42 ` Michael Heerdegen
@ 2022-09-03  8:02   ` Damien Cassou
  0 siblings, 0 replies; 12+ messages in thread
From: Damien Cassou @ 2022-09-03  8:02 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: 57548

Michael Heerdegen <michael_heerdegen@web.de> writes:
> Could this maybe (simpler) call `seq-do-indexed'?

excellent suggestion, thank you. I applied it in the second version of
the patch. I'm sorry I didn't mention it there.

-- 
Damien Cassou

"Success is the ability to go from one failure to another without
losing enthusiasm." --Winston Churchill





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#57548: Add new function `seq-positions'
  2022-09-03  8:01   ` Damien Cassou
@ 2022-09-03 10:16     ` Eli Zaretskii
  2022-09-03 12:27       ` Lars Ingebrigtsen
  2022-09-03 13:03       ` Damien Cassou
  2022-09-04  2:27     ` Michael Heerdegen
  1 sibling, 2 replies; 12+ messages in thread
From: Eli Zaretskii @ 2022-09-03 10:16 UTC (permalink / raw)
  To: Damien Cassou; +Cc: 57548

> From: Damien Cassou <damien@cassou.me>
> Cc: 57548@debbugs.gnu.org
> Date: Sat, 03 Sep 2022 10:01:26 +0200
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> > We use "index", not "position".
> 
> I changed the text in the manual, NEWS and docstring to talk about
> "index" instead of "position".  I kept the word "positions" in the
> function name because there is already a `seq-position` function and the
> 2 are so similar that I think they deserve a similar name. What do you
> think?

LGTM, thanks.

> > In any case, the documentation should explain what you mean by that,
> 
> I haven't found another such explanation in seq.texi so I'm not sure
> what you means.  I would be happy to write something if you feel
> something is still missing though.

As you changed the text to talk about indices, I don't think anything
else is needed.

> > Our style is to say
> >   Equality is defined by the function TESTFN, which defaults to `equal'.
> 
> fixed.  If you want, I can prepare another patch to apply the same
> change to the docstring of the already existing `seq-position`: it
> contains the same phrasing.

Yes, please.  Will be appreciated.

> >> +(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)))))
> >
> > Should we test more than just one type of sequences?
> 
> 
> The `with-test-sequences` call checks 3 types of sequences already as
> far as I understand. Do you mean something else?

No, I've missed that part

Thanks.





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#57548: Add new function `seq-positions'
  2022-09-03 10:16     ` Eli Zaretskii
@ 2022-09-03 12:27       ` Lars Ingebrigtsen
  2022-09-03 13:03       ` Damien Cassou
  1 sibling, 0 replies; 12+ messages in thread
From: Lars Ingebrigtsen @ 2022-09-03 12:27 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Damien Cassou, 57548

Eli Zaretskii <eliz@gnu.org> writes:

>> I changed the text in the manual, NEWS and docstring to talk about
>> "index" instead of "position".  I kept the word "positions" in the
>> function name because there is already a `seq-position` function and the
>> 2 are so similar that I think they deserve a similar name. What do you
>> think?
>
> LGTM, thanks.

Me too.





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#57548: Add new function `seq-positions'
  2022-09-03 10:16     ` Eli Zaretskii
  2022-09-03 12:27       ` Lars Ingebrigtsen
@ 2022-09-03 13:03       ` Damien Cassou
  1 sibling, 0 replies; 12+ messages in thread
From: Damien Cassou @ 2022-09-03 13:03 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 57548

Eli Zaretskii <eliz@gnu.org> writes:
> From: Damien Cassou <damien@cassou.me>
>> If you want, I can prepare another patch to apply the same change to
>> the docstring of the already existing `seq-position`: it contains the
>> same phrasing.
>
> Yes, please.  Will be appreciated.

fixed in bug#57561.

-- 
Damien Cassou

"Success is the ability to go from one failure to another without
losing enthusiasm." --Winston Churchill





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#57548: Add new function `seq-positions'
  2022-09-03  8:01   ` Damien Cassou
  2022-09-03 10:16     ` Eli Zaretskii
@ 2022-09-04  2:27     ` Michael Heerdegen
  2022-09-04  8:44       ` Damien Cassou
  1 sibling, 1 reply; 12+ messages in thread
From: Michael Heerdegen @ 2022-09-04  2:27 UTC (permalink / raw)
  To: Damien Cassou; +Cc: Eli Zaretskii, 57548

Damien Cassou <damien@cassou.me> writes:

> Indeed, such a function would certainly make sense.  I would refrain
> from calling it `seq-positions` though because there is already a
> `seq-position` function that is very similar.  I guess the function you
> suggest wouldn't take an element as argument either.  In any case, I
> think the current function makes sense on its own and what you are
> asking for is a new function that seems out of scope of this current
> patch.  If you give me a name (what about `(seq-matching-positions seq
> pred)`?), I volunteer to send a new patch in a new mail thread.

No - I actually mean (and, sorry, I see that my initial response said
something slightly different): calling the function with ELT == 10 and
TESTFN == #'>= would return all positions in the SEQUENCE of elements
not less than 10.

We don't need another function.  You only need to say that TESTFN is not
necessarily an equality predicate or symmetric.  And in which order the
two arguments are passed (and have a look that your current calling order
is so that the above example works as expected).

Michael.





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#57548: Add new function `seq-positions'
  2022-09-04  2:27     ` Michael Heerdegen
@ 2022-09-04  8:44       ` Damien Cassou
  2022-09-04 11:22         ` Lars Ingebrigtsen
  2022-09-06  1:44         ` Michael Heerdegen
  0 siblings, 2 replies; 12+ messages in thread
From: Damien Cassou @ 2022-09-04  8:44 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: Eli Zaretskii, 57548

[-- Attachment #1: Type: text/plain, Size: 577 bytes --]

Michael Heerdegen <michael_heerdegen@web.de> writes:
> We don't need another function.  You only need to say that TESTFN is not
> necessarily an equality predicate or symmetric.  And in which order the
> two arguments are passed (and have a look that your current calling order
> is so that the above example works as expected).

Very clear. See attached patch for the new version. I also added a unit
test to make sure the result is what you expect.

-- 
Damien Cassou

"Success is the ability to go from one failure to another without
losing enthusiasm." --Winston Churchill

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-new-function-seq-positions.patch --]
[-- Type: text/x-patch, Size: 4454 bytes --]

From bb262f675f22638d1523685c57bef130bd0cc74e Mon Sep 17 00:00:00 2001
From: Damien Cassou <damien@cassou.me>
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


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* bug#57548: Add new function `seq-positions'
  2022-09-04  8:44       ` Damien Cassou
@ 2022-09-04 11:22         ` Lars Ingebrigtsen
  2022-09-06  1:44         ` Michael Heerdegen
  1 sibling, 0 replies; 12+ messages in thread
From: Lars Ingebrigtsen @ 2022-09-04 11:22 UTC (permalink / raw)
  To: Damien Cassou; +Cc: Michael Heerdegen, Eli Zaretskii, 57548

Damien Cassou <damien@cassou.me> writes:

> Very clear. See attached patch for the new version. I also added a unit
> test to make sure the result is what you expect.

Thanks; pushed to Emacs 29.






^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#57548: Add new function `seq-positions'
  2022-09-04  8:44       ` Damien Cassou
  2022-09-04 11:22         ` Lars Ingebrigtsen
@ 2022-09-06  1:44         ` Michael Heerdegen
  1 sibling, 0 replies; 12+ messages in thread
From: Michael Heerdegen @ 2022-09-06  1:44 UTC (permalink / raw)
  To: Damien Cassou; +Cc: Eli Zaretskii, 57548

Damien Cassou <damien@cassou.me> writes:

> Very clear. See attached patch for the new version. I also added a unit
> test to make sure the result is what you expect.

Ok, great, thanks.

> +TESTFN is a two-argument function which is passed each element of
> +SEQUENCE as first argument and ELT as second. TESTFN defaults to
> +`equal'.

I hope this order feels natural for everyone?  `pred` in `pcase' does
the opposite, but there the predicate comes first (syntactically).
We don't need to have the order analogous to pcase - but if people
prefer ELT being the first element in the TESTFN call, we can still
change it.

Michael.





^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2022-09-06  1:44 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-09-02 18:43 bug#57548: Add new function `seq-positions' Damien Cassou
2022-09-02 19:00 ` Eli Zaretskii
2022-09-03  8:01   ` Damien Cassou
2022-09-03 10:16     ` Eli Zaretskii
2022-09-03 12:27       ` Lars Ingebrigtsen
2022-09-03 13:03       ` Damien Cassou
2022-09-04  2:27     ` Michael Heerdegen
2022-09-04  8:44       ` Damien Cassou
2022-09-04 11:22         ` Lars Ingebrigtsen
2022-09-06  1:44         ` Michael Heerdegen
2022-09-03  1:42 ` Michael Heerdegen
2022-09-03  8:02   ` Damien Cassou

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).