From 87c8b91e1bf439bcea65fa96f9253b59b5ec52bc Mon Sep 17 00:00:00 2001 From: Matus Goljer Date: Sun, 18 Aug 2013 19:15:15 +0200 Subject: [PATCH] Add indexing operations --- README.md | 92 +++++++++++++++++++++++++++++++++++++++++++++++++ dash.el | 68 ++++++++++++++++++++++++++++++++++++ dev/examples.el | 34 ++++++++++++++++++ 3 files changed, 194 insertions(+) diff --git a/README.md b/README.md index 7dcb490..ba03abe 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,16 @@ To get function combinators: * [-partition-by-header](#-partition-by-header-fn-list) `(fn list)` * [-group-by](#-group-by-fn-list) `(fn list)` +### Indexing + +* [-elem-index](#-elem-index-elem-list) `(elem list)` +* [-elem-indices](#-elem-indices-elem-list) `(elem list)` +* [-find-index](#-find-index-pred-list) `(pred list)` +* [-find-indices](#-find-indices-pred-list) `(pred list)` +* [-select-by-indices](#-select-by-indices-indices-list) `(indices list)` +* [-grade-up](#-grade-up-comparator-list) `(comparator list)` +* [-grade-down](#-grade-down-comparator-list) `(comparator list)` + ### Set operations * [-union](#-union-list-list2) `(list list2)` @@ -678,6 +688,88 @@ elements of `list`. Keys are compared by `equal`. ``` +## Indexing + +#### -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. + +```cl +(-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 +``` + +#### -elem-indices `(elem list)` + +Return the indices of all elements in `list` equal to the query +element `elem`, in ascending order. + +```cl +(-elem-indices 2 '(6 7 8 2 3 4 2 1)) ;; => '(3 6) +(-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)` + +Return the indices of all elements in `list` satisfying the +predicate `pred`, in ascending order. + +```cl +(-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-indices `(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. + +```cl +(-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) +``` + +#### -select-by-indices `(indices list)` + +Return a list whose elements are elements from `list` selected +as `(nth i list)` for all i from `indices`. + +```cl +(-select-by-indices '(4 10 2 3 6) '("v" "e" "l" "o" "c" "i" "r" "a" "p" "t" "o" "r")) ;; => '("c" "o" "l" "o" "r") +(-select-by-indices '(2 1 0) '("a" "b" "c")) ;; => '("c" "b" "a") +(-select-by-indices '(0 1 2 0 1 3 3 1) '("f" "a" "r" "l")) ;; => '("f" "a" "r" "f" "a" "l" "l" "a") +``` + +#### -grade-up `(comparator list)` + +Grades elements of `list` using `comparator` relation, yielding a +permutation vector such that applying this permutation to `list` +sorts it in ascending order. + +```cl +(-grade-up '< '(3 1 4 2 1 3 3)) ;; => '(1 4 3 0 5 6 2) +(let ((l '(3 1 4 2 1 3 3))) (-select-by-indices (-grade-up '< l) l)) ;; => '(1 1 2 3 3 3 4) +``` + +#### -grade-down `(comparator list)` + +Grades elements of `list` using `comparator` relation, yielding a +permutation vector such that applying this permutation to `list` +sorts it in descending order. + +```cl +(-grade-down '< '(3 1 4 2 1 3 3)) ;; => '(2 0 5 6 3 1 4) +(let ((l '(3 1 4 2 1 3 3))) (-select-by-indices (-grade-down '< l) l)) ;; => '(4 3 3 3 2 1 1) +``` + + ## Set operations #### -union `(list list2)` diff --git a/dash.el b/dash.el index 970b743..42c741a 100644 --- a/dash.el +++ b/dash.el @@ -697,6 +697,65 @@ When called, the returned function calls FN with ARGS first and then additional args." (apply 'apply-partially fn args)) +(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." + (car (-elem-indices elem list))) + +(defun -elem-indices (elem list) + "Return the indices of all elements in LIST equal to the query +element ELEM, in ascending order." + (-find-indices (-partial 'equal elem) list)) + +(defun -find-indices (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." + (let ((i 0)) + (apply 'append (--map-indexed (when (funcall pred it) (list it-index)) list)))) + +(defmacro --find-indices (form list) + "Anaphoric version of `-find-indices'." + `(-find-indices (lambda (it) ,form) ,list)) + +(defun -find-index (pred list) + "Return the indices of all elements in LIST satisfying the +predicate PRED, in ascending order." + (car (-find-indices pred list))) + +(defmacro --find-index (form list) + "Anaphoric version of `-find-index'." + `(-find-index (lambda (it) ,form) ,list)) + +(defun -select-by-indices (indices list) + "Return a list whose elements are elements from LIST selected +as `(nth i list)` for all i from INDICES." + (let (r) + (--each indices + (!cons (nth it list) r)) + (nreverse r))) + +(defun -grade-up (comparator list) + "Grades elements of LIST using COMPARATOR relation, yielding a +permutation vector such that applying this permutation to LIST +sorts it in ascending order." + ;; ugly hack to "fix" lack of lexical scope + (let ((comp `(lambda (it other) (funcall ',comparator (car it) (car other))))) + (->> (--map-indexed (cons it it-index) list) + (-sort comp) + (-map 'cdr)))) + +(defun -grade-down (comparator list) + "Grades elements of LIST using COMPARATOR relation, yielding a +permutation vector such that applying this permutation to LIST +sorts it in descending order." + ;; ugly hack to "fix" lack of lexical scope + (let ((comp `(lambda (it other) (funcall ',comparator (car other) (car it))))) + (->> (--map-indexed (cons it it-index) list) + (-sort comp) + (-map 'cdr)))) + (defmacro -> (x &optional form &rest more) "Threads the expr through the forms. Inserts X as the second item in the first form, making a list of it if it is not a list @@ -1010,6 +1069,15 @@ The items for the comparator form are exposed as \"it\" and \"other\"." "-cut" "-orfn" "-andfn" + "-elem-index" + "-elem-indices" + "-find-indices" + "--find-indices" + "-find-index" + "--find-index" + "-select-by-indices" + "-grade-up" + "-grade-down" "->" "->>" "-->" diff --git a/dev/examples.el b/dev/examples.el index 5430057..6c3868e 100644 --- a/dev/examples.el +++ b/dev/examples.el @@ -238,6 +238,40 @@ (-group-by 'even? '(1 1 2 2 2 3 4 6 8)) => '((nil . (1 1 3)) (t . (2 2 2 4 6 8))) (--group-by (car (split-string it "/")) '("a/b" "c/d" "a/e")) => '(("a" . ("a/b" "a/e")) ("c" . ("c/d"))))) +(def-example-group "Indexing" nil + (defexamples -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) + + (defexamples -elem-indices + (-elem-indices 2 '(6 7 8 2 3 4 2 1)) => '(3 6) + (-elem-indices "bar" '("foo" "bar" "baz")) => '(1) + (-elem-indices '(1 2) '((3) (1 2) (5 6) (1 2) nil)) => '(1 3)) + + (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) + + (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)) + + (defexamples -select-by-indices + (-select-by-indices '(4 10 2 3 6) '("v" "e" "l" "o" "c" "i" "r" "a" "p" "t" "o" "r")) => '("c" "o" "l" "o" "r") + (-select-by-indices '(2 1 0) '("a" "b" "c")) => '("c" "b" "a") + (-select-by-indices '(0 1 2 0 1 3 3 1) '("f" "a" "r" "l")) => '("f" "a" "r" "f" "a" "l" "l" "a")) + + (defexamples -grade-up + (-grade-up '< '(3 1 4 2 1 3 3)) => '(1 4 3 0 5 6 2) + (let ((l '(3 1 4 2 1 3 3))) (-select-by-indices (-grade-up '< l) l)) => '(1 1 2 3 3 3 4)) + + (defexamples -grade-down + (-grade-down '< '(3 1 4 2 1 3 3)) => '(2 0 5 6 3 1 4) + (let ((l '(3 1 4 2 1 3 3))) (-select-by-indices (-grade-down '< l) l)) => '(4 3 3 3 2 1 1))) + (def-example-group "Set operations" nil (defexamples -union (-union '(1 2 3) '(3 4 5)) => '(1 2 3 4 5)