summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJSDurand <mmemmew@gmail.com>2021-06-22 01:10:52 +0800
committerJSDurand <mmemmew@gmail.com>2021-06-22 01:10:52 +0800
commit834d17574ac5fa7e9533ff86709f6f8637898085 (patch)
treea60c7204c93d0d338622e09ff8bc2fa3711bf6d2
parent2581527ae37a054a9f662d117a2e3e434cdb906c (diff)
common: member and delete-dups convenience.
* common.el (durand-delete-dups): This uses a custom hash-table to test for the equality. Hence it can support custom equality-tests. This function grew out of a function I wrote for eshell. (durand-member): This is a convenient function to test for membership in a list. The implementationi is nothing new. But it is convenient.
-rw-r--r--common.el62
1 files changed, 62 insertions, 0 deletions
diff --git a/common.el b/common.el
index b17787b..0417da5 100644
--- a/common.el
+++ b/common.el
@@ -331,6 +331,68 @@ completely hiding it."
(list (or ,light ""))))))
(`(setcdr (assq ',minor minor-mode-alist) (list (or ,light ""))))))
+;;;###autoload
+(defun durand-delete-dups (sequence &rest args)
+ "Delete duplicate elements in SEQUENCE.
+If the keyword argument TEST is non-nil, it should be a function
+with two arguments which tests for equality of elements in the
+sequence. The default is the function `equal'.
+
+If the keyword argument KEY is non-nil, it should be a function
+with one argument which returns the key of the element in the
+sequence to be compared by the test function. The default is the
+function `identity'.
+
+Note that this function is not supposed to change global state,
+including match data, so the functions in TEST and KEY are
+supposed to leave the global state alone as well.
+
+\(fn SEQUENCE &key TEST KEY)"
+ (declare (pure t) (side-effect-free t))
+ (let* ((len (length sequence))
+ (temp-obarray (obarray-make len))
+ (valid-key-num (+ (cond ((plist-member args :key) 1) (0))
+ (cond ((plist-member args :test) 1) (0))))
+ (key (cond ((cadr (plist-member args :key)))
+ (#'identity)))
+ (test-fn (cond ((cadr (plist-member args :test)))
+ (#'equal)))
+ found-table result)
+ (cond ((or (= (mod (length args) 2) 1)
+ (> (length args) (* 2 valid-key-num)))
+ (user-error "Invalid keyword arguments. Only :key and :test are allowed, but got %S"
+ args)))
+ ;; Note: This just puts a property to the symbol.
+ (define-hash-table-test 'durand-delete-dups-test
+ test-fn (function (lambda (obj) (intern (format "%S" obj) temp-obarray))))
+ (setq found-table (make-hash-table :test 'durand-delete-dups-test :size len))
+ (mapc
+ (function
+ (lambda (element)
+ (cond ((gethash (funcall key element) found-table))
+ ;; Abuse the fact that `puthash' always returns VALUE.
+ ((puthash (funcall key element) t found-table)
+ (setq result (cons element result))))))
+ sequence)
+ (nreverse result)))
+
+;;;###autoload
+(defun durand-member (element ls &optional test)
+ "Return the tail of LS whose car is ELT.
+Comparison is done by TEST if that is non-nil, else by `equal'."
+ (let ((temp (copy-tree ls))
+ (not-found t))
+ (while (and not-found (consp temp))
+ (cond
+ ((and test
+ (funcall test element (car temp)))
+ (setq not-found nil))
+ (test (setq temp (cdr temp)))
+ ((equal element (car temp))
+ (setq not-found nil))
+ ((setq temp (cdr temp)))))
+ temp))
+
(provide 'common)
;;; common.el ends here.