diff options
author | JSDurand <mmemmew@gmail.com> | 2021-06-22 01:10:52 +0800 |
---|---|---|
committer | JSDurand <mmemmew@gmail.com> | 2021-06-22 01:10:52 +0800 |
commit | 834d17574ac5fa7e9533ff86709f6f8637898085 (patch) | |
tree | a60c7204c93d0d338622e09ff8bc2fa3711bf6d2 | |
parent | 2581527ae37a054a9f662d117a2e3e434cdb906c (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.el | 62 |
1 files changed, 62 insertions, 0 deletions
@@ -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. |