Improve element finding

* dash.el (-first): Contrast -first with -first-item, not car, in
docstring.
(-elem-index, --find-index, --find-last-index): Avoid constructing a
list of results just to throw most of them away.  Suggested by
Philippe Vaucher (#394).
(--find-indices): Use --keep for speed.
(-elem-indices, -find-indices): Use --find-indices for speed.
(-find-index, -find-last-index): Use anaphoric counterpart for
speed.

* dev/examples.el (-elem-index, -elem-indices, -find-index)
(-find-last-index, -find-indices): Extend tests.

* README.md:
* dash.texi: Regenerate docs.

Fixes #394.
master
Basil L. Contovounesios 4 years ago
parent 799a308a08
commit ae0ce7959e
No known key found for this signature in database
GPG Key ID: 205AB54A5D5D8CFF
  1. 75
      README.md
  2. 110
      dash.el
  3. 93
      dash.texi
  4. 112
      dev/examples.el

@ -1669,64 +1669,83 @@ related predicates.
#### -elem-index `(elem list)`
Return the index of the first element in the given `list` which
is equal to the query element `elem`, or `nil` if there is no
such element.
Return the first index of `elem` in `list`.
That is, the index within `list` of the first element that is
`equal` to `elem`. Return `nil` if there is no such element.
See also: [`-find-index`](#-find-index-pred-list).
```el
(-elem-index 2 '(6 7 8 2 3 4)) ;; => 3
(-elem-index 2 '(6 7 8 3 4)) ;; => nil
(-elem-index "bar" '("foo" "bar" "baz")) ;; => 1
(-elem-index '(1 2) '((3) (5 6) (1 2) nil)) ;; => 2
```
#### -elem-indices `(elem list)`
Return the indices of all elements in `list` equal to the query
element `elem`, in ascending order.
Return the list of indices at which `elem` appears in `list`.
That is, the indices of all elements of `list` `equal` to `elem`, in
the same ascending order as they appear in `list`.
```el
(-elem-indices 2 '(6 7 8 2 3 4 2 1)) ;; => (3 6)
(-elem-indices 2 '(6 7 8 3 4 1)) ;; => ()
(-elem-indices "bar" '("foo" "bar" "baz")) ;; => (1)
(-elem-indices '(1 2) '((3) (1 2) (5 6) (1 2) nil)) ;; => (1 3)
```
#### -find-index `(pred list)`
Take a predicate `pred` and a `list` and return the index of the
first element in the list satisfying the predicate, or `nil` if
there is no such element.
Return the index of the first item satisfying `pred` in `list`.
Return `nil` if no such item is found.
`pred` is called with one argument, the current list element, until
it returns non-`nil`, at which point the search terminates.
This function's anaphoric counterpart is `--find-index`.
See also [`-first`](#-first-pred-list).
See also: [`-first`](#-first-pred-list), [`-find-last-index`](#-find-last-index-pred-list).
```el
(-find-index 'even? '(2 4 1 6 3 3 5 8)) ;; => 0
(--find-index (< 5 it) '(2 4 1 6 3 3 5 8)) ;; => 3
(-find-index (-partial 'string-lessp "baz") '("bar" "foo" "baz")) ;; => 1
(-find-index #'numberp '(a b c)) ;; => nil
(-find-index #'natnump '(1 0 -1)) ;; => 0
(--find-index (> it 5) '(2 4 1 6 3 3 5 8)) ;; => 3
```
#### -find-last-index `(pred list)`
Take a predicate `pred` and a `list` and return the index of the
last element in the list satisfying the predicate, or `nil` if
there is no such element.
Return the index of the last item satisfying `pred` in `list`.
Return `nil` if no such item is found.
Predicate `pred` is called with one argument each time, namely the
current list element.
This function's anaphoric counterpart is `--find-last-index`.
See also [`-last`](#-last-pred-list).
See also: [`-last`](#-last-pred-list), [`-find-index`](#-find-index-pred-list).
```el
(-find-last-index 'even? '(2 4 1 6 3 3 5 8)) ;; => 7
(--find-last-index (< 5 it) '(2 7 1 6 3 8 5 2)) ;; => 5
(-find-last-index (-partial 'string-lessp "baz") '("q" "foo" "baz")) ;; => 1
(-find-last-index #'numberp '(a b c)) ;; => nil
(--find-last-index (> it 5) '(2 7 1 6 3 8 5 2)) ;; => 5
(-find-last-index (-partial #'string< 'a) '(c b a)) ;; => 1
```
#### -find-indices `(pred list)`
Return the indices of all elements in `list` satisfying the
predicate `pred`, in ascending order.
Return the list of indices in `list` satisfying `pred`.
Each element of `list` in turn is passed to `pred`. If the result is
non-`nil`, the index of that element in `list` is included in the
result. The returned indices are in ascending order, i.e., in
the same order as they appear in `list`.
This function's anaphoric counterpart is `--find-indices`.
See also: [`-find-index`](#-find-index-pred-list), [`-elem-indices`](#-elem-indices-elem-list).
```el
(-find-indices 'even? '(2 4 1 6 3 3 5 8)) ;; => (0 1 3 7)
(--find-indices (< 5 it) '(2 4 1 6 3 3 5 8)) ;; => (3 7)
(-find-indices (-partial 'string-lessp "baz") '("bar" "foo" "baz")) ;; => (1)
(-find-indices #'numberp '(a b c)) ;; => ()
(-find-indices #'numberp '(8 1 d 2 b c a 3)) ;; => (0 1 3 7)
(--find-indices (> it 5) '(2 4 1 6 3 3 5 8)) ;; => (3 7)
```
#### -grade-up `(comparator list)`
@ -2069,7 +2088,9 @@ See also: [`-flatten-n`](#-flatten-n-num-list), [`-table`](#-table-fn-rest-lists
Return the first item in `list` for which `pred` returns non-`nil`.
Return `nil` if no such element is found.
To get the first item in the list no questions asked, use `car`.
To get the first item in the list no questions asked,
use [`-first-item`](#-first-item-list).
Alias: `-find`.

@ -858,14 +858,16 @@ This is the anaphoric counterpart to `-first'."
(defun -first (pred list)
"Return the first item in LIST for which PRED returns non-nil.
Return nil if no such element is found.
To get the first item in the list no questions asked, use `car'.
To get the first item in the list no questions asked,
use `-first-item'.
Alias: `-find'.
This function's anaphoric counterpart is `--first'."
(--first (funcall pred it) list))
(defalias '-find '-first)
(defalias '-find #'-first)
(defalias '--find '--first)
(defmacro --some (form list)
@ -1780,54 +1782,88 @@ See also: `-flatten-n', `-table'"
(dash--table-carry lists restore-lists)))
(nreverse re)))
(defmacro --find-index (form list)
"Return the first index in LIST for which FORM evals to non-nil.
Return nil if no such index is found.
Each element of LIST in turn is bound to `it' and its index
within LIST to `it-index' before evaluating FORM.
This is the anaphoric counterpart to `-find-index'."
(declare (debug (form form)))
`(--some (and ,form it-index) ,list))
(defun -find-index (pred list)
"Return the index of the first item satisfying PRED in LIST.
Return nil if no such item is found.
PRED is called with one argument, the current list element, until
it returns non-nil, at which point the search terminates.
This function's anaphoric counterpart is `--find-index'.
See also: `-first', `-find-last-index'."
(--find-index (funcall pred it) list))
(defun -elem-index (elem list)
"Return the index of the first element in the given LIST which
is equal to the query element ELEM, or nil if there is no
such element."
(declare (pure t) (side-effect-free t))
(car (-elem-indices elem list)))
"Return the first index of ELEM in LIST.
That is, the index within LIST of the first element that is
`equal' to ELEM. Return nil if there is no such element.
(defun -elem-indices (elem list)
"Return the indices of all elements in LIST equal to the query
element ELEM, in ascending order."
See also: `-find-index'."
(declare (pure t) (side-effect-free t))
(-find-indices (-partial 'equal elem) list))
(--find-index (equal elem it) list))
(defmacro --find-indices (form list)
"Return the list of indices in LIST for which FORM evals to non-nil.
Each element of LIST in turn is bound to `it' and its index
within LIST to `it-index' before evaluating FORM.
This is the anaphoric counterpart to `-find-indices'."
(declare (debug (form form)))
`(--keep (and ,form it-index) ,list))
(defun -find-indices (pred list)
"Return the indices of all elements in LIST satisfying the
predicate PRED, in ascending order."
(apply 'append (--map-indexed (when (funcall pred it) (list it-index)) list)))
"Return the list of indices in LIST satisfying PRED.
(defmacro --find-indices (form list)
"Anaphoric version of `-find-indices'."
(declare (debug (def-form form)))
`(-find-indices (lambda (it) (ignore it) ,form) ,list))
Each element of LIST in turn is passed to PRED. If the result is
non-nil, the index of that element in LIST is included in the
result. The returned indices are in ascending order, i.e., in
the same order as they appear in LIST.
(defun -find-index (pred list)
"Take a predicate PRED and a LIST and return the index of the
first element in the list satisfying the predicate, or nil if
there is no such element.
This function's anaphoric counterpart is `--find-indices'.
See also `-first'."
(car (-find-indices pred list)))
See also: `-find-index', `-elem-indices'."
(--find-indices (funcall pred it) list))
(defmacro --find-index (form list)
"Anaphoric version of `-find-index'."
(declare (debug (def-form form)))
`(-find-index (lambda (it) (ignore it) ,form) ,list))
(defun -elem-indices (elem list)
"Return the list of indices at which ELEM appears in LIST.
That is, the indices of all elements of LIST `equal' to ELEM, in
the same ascending order as they appear in LIST."
(declare (pure t) (side-effect-free t))
(--find-indices (equal elem it) list))
(defmacro --find-last-index (form list)
"Return the last index in LIST for which FORM evals to non-nil.
Return nil if no such index is found.
Each element of LIST in turn is bound to `it' and its index
within LIST to `it-index' before evaluating FORM.
This is the anaphoric counterpart to `-find-last-index'."
(declare (debug (form form)))
(let ((i (make-symbol "index")))
`(let (,i)
(--each ,list
(when ,form (setq ,i it-index)))
,i)))
(defun -find-last-index (pred list)
"Take a predicate PRED and a LIST and return the index of the
last element in the list satisfying the predicate, or nil if
there is no such element.
"Return the index of the last item satisfying PRED in LIST.
Return nil if no such item is found.
See also `-last'."
(-last-item (-find-indices pred list)))
Predicate PRED is called with one argument each time, namely the
current list element.
(defmacro --find-last-index (form list)
"Anaphoric version of `-find-last-index'."
(declare (debug (def-form form)))
`(-find-last-index (lambda (it) (ignore it) ,form) ,list))
This function's anaphoric counterpart is `--find-last-index'.
See also: `-last', `-find-index'."
(--find-last-index (funcall pred it) list))
(defun -select-by-indices (indices list)
"Return a list whose elements are elements from LIST selected

@ -2395,14 +2395,16 @@ related predicates.
@anchor{-elem-index}
@defun -elem-index (elem list)
Return the index of the first element in the given @var{list} which
is equal to the query element @var{elem}, or @code{nil} if there is no
such element.
Return the first index of @var{elem} in @var{list}.
That is, the index within @var{list} of the first element that is
@code{equal} to @var{elem}. Return @code{nil} if there is no such element.
See also: @code{-find-index} (@pxref{-find-index}).
@example
@group
(-elem-index 2 '(6 7 8 2 3 4))
@result{} 3
(-elem-index 2 '(6 7 8 3 4))
@result{} nil
@end group
@group
(-elem-index "bar" '("foo" "bar" "baz"))
@ -2417,13 +2419,14 @@ such element.
@anchor{-elem-indices}
@defun -elem-indices (elem list)
Return the indices of all elements in @var{list} equal to the query
element @var{elem}, in ascending order.
Return the list of indices at which @var{elem} appears in @var{list}.
That is, the indices of all elements of @var{list} @code{equal} to @var{elem}, in
the same ascending order as they appear in @var{list}.
@example
@group
(-elem-indices 2 '(6 7 8 2 3 4 2 1))
@result{} (3 6)
(-elem-indices 2 '(6 7 8 3 4 1))
@result{} ()
@end group
@group
(-elem-indices "bar" '("foo" "bar" "baz"))
@ -2438,47 +2441,55 @@ element @var{elem}, in ascending order.
@anchor{-find-index}
@defun -find-index (pred list)
Take a predicate @var{pred} and a @var{list} and return the index of the
first element in the list satisfying the predicate, or @code{nil} if
there is no such element.
Return the index of the first item satisfying @var{pred} in @var{list}.
Return @code{nil} if no such item is found.
See also @code{-first} (@pxref{-first}).
@var{pred} is called with one argument, the current list element, until
it returns non-@code{nil}, at which point the search terminates.
This function's anaphoric counterpart is @code{--find-index}.
See also: @code{-first} (@pxref{-first}), @code{-find-last-index} (@pxref{-find-last-index}).
@example
@group
(-find-index 'even? '(2 4 1 6 3 3 5 8))
@result{} 0
(-find-index #'numberp '(a b c))
@result{} nil
@end group
@group
(--find-index (< 5 it) '(2 4 1 6 3 3 5 8))
@result{} 3
(-find-index #'natnump '(1 0 -1))
@result{} 0
@end group
@group
(-find-index (-partial 'string-lessp "baz") '("bar" "foo" "baz"))
@result{} 1
(--find-index (> it 5) '(2 4 1 6 3 3 5 8))
@result{} 3
@end group
@end example
@end defun
@anchor{-find-last-index}
@defun -find-last-index (pred list)
Take a predicate @var{pred} and a @var{list} and return the index of the
last element in the list satisfying the predicate, or @code{nil} if
there is no such element.
Return the index of the last item satisfying @var{pred} in @var{list}.
Return @code{nil} if no such item is found.
Predicate @var{pred} is called with one argument each time, namely the
current list element.
See also @code{-last} (@pxref{-last}).
This function's anaphoric counterpart is @code{--find-last-index}.
See also: @code{-last} (@pxref{-last}), @code{-find-index} (@pxref{-find-index}).
@example
@group
(-find-last-index 'even? '(2 4 1 6 3 3 5 8))
@result{} 7
(-find-last-index #'numberp '(a b c))
@result{} nil
@end group
@group
(--find-last-index (< 5 it) '(2 7 1 6 3 8 5 2))
(--find-last-index (> it 5) '(2 7 1 6 3 8 5 2))
@result{} 5
@end group
@group
(-find-last-index (-partial 'string-lessp "baz") '("q" "foo" "baz"))
(-find-last-index (-partial #'string< 'a) '(c b a))
@result{} 1
@end group
@end example
@ -2486,21 +2497,29 @@ See also @code{-last} (@pxref{-last}).
@anchor{-find-indices}
@defun -find-indices (pred list)
Return the indices of all elements in @var{list} satisfying the
predicate @var{pred}, in ascending order.
Return the list of indices in @var{list} satisfying @var{pred}.
Each element of @var{list} in turn is passed to @var{pred}. If the result is
non-@code{nil}, the index of that element in @var{list} is included in the
result. The returned indices are in ascending order, i.e., in
the same order as they appear in @var{list}.
This function's anaphoric counterpart is @code{--find-indices}.
See also: @code{-find-index} (@pxref{-find-index}), @code{-elem-indices} (@pxref{-elem-indices}).
@example
@group
(-find-indices 'even? '(2 4 1 6 3 3 5 8))
@result{} (0 1 3 7)
(-find-indices #'numberp '(a b c))
@result{} ()
@end group
@group
(--find-indices (< 5 it) '(2 4 1 6 3 3 5 8))
@result{} (3 7)
(-find-indices #'numberp '(8 1 d 2 b c a 3))
@result{} (0 1 3 7)
@end group
@group
(-find-indices (-partial 'string-lessp "baz") '("bar" "foo" "baz"))
@result{} (1)
(--find-indices (> it 5) '(2 4 1 6 3 3 5 8))
@result{} (3 7)
@end group
@end example
@end defun
@ -3072,7 +3091,9 @@ See also: @code{-flatten-n} (@pxref{-flatten-n}), @code{-table} (@pxref{-table})
@defun -first (pred list)
Return the first item in @var{list} for which @var{pred} returns non-@code{nil}.
Return @code{nil} if no such element is found.
To get the first item in the list no questions asked, use @code{car}.
To get the first item in the list no questions asked,
use @code{-first-item} (@pxref{-first-item}).
Alias: @code{-find}.

@ -989,29 +989,115 @@ value rather than consuming a list to produce a single value."
related predicates."
(defexamples -elem-index
(-elem-index 2 '(6 7 8 2 3 4)) => 3
(-elem-index 2 '(6 7 8 3 4)) => nil
(-elem-index "bar" '("foo" "bar" "baz")) => 1
(-elem-index '(1 2) '((3) (5 6) (1 2) nil)) => 2)
(-elem-index '(1 2) '((3) (5 6) (1 2) nil)) => 2
(-elem-index nil ()) => nil
(-elem-index nil '(t)) => nil
(-elem-index nil '(nil)) => 0
(-elem-index nil '(nil t)) => 0
(-elem-index nil '(t nil)) => 1
(-elem-index t ()) => nil
(-elem-index t '(nil)) => nil
(-elem-index t '(t)) => 0
(-elem-index t '(t nil)) => 0
(-elem-index t '(nil t)) => 1)
(defexamples -elem-indices
(-elem-indices 2 '(6 7 8 2 3 4 2 1)) => '(3 6)
(-elem-indices 2 '(6 7 8 3 4 1)) => '()
(-elem-indices "bar" '("foo" "bar" "baz")) => '(1)
(-elem-indices '(1 2) '((3) (1 2) (5 6) (1 2) nil)) => '(1 3))
(-elem-indices '(1 2) '((3) (1 2) (5 6) (1 2) nil)) => '(1 3)
(-elem-indices nil ()) => ()
(-elem-indices nil '(t)) => ()
(-elem-indices nil '(nil)) => '(0)
(-elem-indices nil '(nil t)) => '(0)
(-elem-indices nil '(t nil)) => '(1)
(-elem-indices nil '(t t)) => ()
(-elem-indices nil '(nil nil)) => '(0 1)
(-elem-indices t ()) => ()
(-elem-indices t '(t)) => '(0)
(-elem-indices t '(nil)) => ()
(-elem-indices t '(nil t)) => '(1)
(-elem-indices t '(t nil)) => '(0)
(-elem-indices t '(t t)) => '(0 1)
(-elem-indices t '(nil nil)) => ())
(defexamples -find-index
(-find-index 'even? '(2 4 1 6 3 3 5 8)) => 0
(--find-index (< 5 it) '(2 4 1 6 3 3 5 8)) => 3
(-find-index (-partial 'string-lessp "baz") '("bar" "foo" "baz")) => 1)
(-find-index #'numberp '(a b c)) => nil
(-find-index #'natnump '(1 0 -1)) => 0
(--find-index (> it 5) '(2 4 1 6 3 3 5 8)) => 3
(-find-index (-cut string< "baz" <>) '("bar" "foo" "baz")) => 1
(--find-index nil ()) => nil
(--find-index nil '(5)) => nil
(--find-index nil '(5 6 7)) => nil
(--find-index t ()) => nil
(--find-index t '(5)) => 0
(--find-index t '(5 . 6)) => 0
(--find-index t '(5 6 7)) => 0
(let (x) (--find-index (setq x it) ()) x) => nil
(let (x) (--find-index (setq x it) '(5)) x) => 5
(let (x) (--find-index (setq x it) '(5 6 7)) x) => 5
(let (x) (--find-index (ignore (setq x it)) ()) x) => nil
(let (x) (--find-index (ignore (setq x it)) '(5)) x) => 5
(let (x) (--find-index (ignore (setq x it)) '(5 6 7)) x) => 7)
(defexamples -find-last-index
(-find-last-index 'even? '(2 4 1 6 3 3 5 8)) => 7
(--find-last-index (< 5 it) '(2 7 1 6 3 8 5 2)) => 5
(-find-last-index (-partial 'string-lessp "baz") '("q" "foo" "baz")) => 1)
(-find-last-index #'numberp '(a b c)) => nil
(--find-last-index (> it 5) '(2 7 1 6 3 8 5 2)) => 5
(-find-last-index (-partial #'string< 'a) '(c b a)) => 1
(--find-last-index nil ()) => nil
(--find-last-index nil '(t)) => nil
(--find-last-index nil '(nil)) => nil
(--find-last-index nil '(nil nil)) => nil
(--find-last-index nil '(nil t)) => nil
(--find-last-index nil '(t nil)) => nil
(--find-last-index nil '(t t)) => nil
(--find-last-index t ()) => nil
(--find-last-index t '(t)) => 0
(--find-last-index t '(nil)) => 0
(--find-last-index t '(nil nil)) => 1
(--find-last-index t '(nil t)) => 1
(--find-last-index t '(t nil)) => 1
(--find-last-index t '(t t)) => 1
(--find-last-index it ()) => nil
(--find-last-index it '(t)) => 0
(--find-last-index it '(nil)) => nil
(--find-last-index it '(nil nil)) => nil
(--find-last-index it '(nil t)) => 1
(--find-last-index it '(t nil)) => 0
(--find-last-index it '(t t)) => 1)
(defexamples -find-indices
(-find-indices 'even? '(2 4 1 6 3 3 5 8)) => '(0 1 3 7)
(--find-indices (< 5 it) '(2 4 1 6 3 3 5 8)) => '(3 7)
(-find-indices (-partial 'string-lessp "baz") '("bar" "foo" "baz")) => '(1))
(-find-indices #'numberp '(a b c)) => '()
(-find-indices #'numberp '(8 1 d 2 b c a 3)) => '(0 1 3 7)
(--find-indices (> it 5) '(2 4 1 6 3 3 5 8)) => '(3 7)
(--find-indices (string< "baz" it) '("bar" "foo" "baz")) => '(1)
(--find-indices nil ()) => ()
(--find-indices nil '(1)) => ()
(--find-indices nil '(nil)) => ()
(--find-indices t ()) => ()
(--find-indices t '(1)) => '(0)
(--find-indices t '(nil)) => '(0)
(--find-indices t '(1 2)) => '(0 1)
(--find-indices t '(nil nil)) => '(0 1)
(--find-indices it ()) => ()
(--find-indices it '(1)) => '(0)
(--find-indices it '(nil)) => ()
(--find-indices it '(1 2)) => '(0 1)
(--find-indices it '(nil nil)) => ()
(-find-indices #'ignore ()) => ()
(-find-indices #'ignore '(1)) => ()
(-find-indices #'ignore '(nil)) => ()
(-find-indices (-andfn) ()) => ()
(-find-indices (-andfn) '(1)) => '(0)
(-find-indices (-andfn) '(nil)) => '(0)
(-find-indices (-andfn) '(1 2)) => '(0 1)
(-find-indices (-andfn) '(nil nil)) => '(0 1)
(-find-indices #'identity ()) => ()
(-find-indices #'identity '(1)) => '(0)
(-find-indices #'identity '(nil)) => ()
(-find-indices #'identity '(1 2)) => '(0 1)
(-find-indices #'identity '(nil nil)) => ())
(defexamples -grade-up
(-grade-up #'< '(3 1 4 2 1 3 3)) => '(1 4 3 0 5 6 2)

Loading…
Cancel
Save