ht.el-2.3/0000755000175000017500000000000014001506323012214 5ustar dogslegdogsleght.el-2.3/test/0000755000175000017500000000000014001506323013173 5ustar dogslegdogsleght.el-2.3/test/ht-test.el0000644000175000017500000002521314001506323015110 0ustar dogslegdogsleg(require 'ht) (ert-deftest ht-test-ht () (let ((test-table (ht (1 2) ("foo" (1+ 2))))) (should (and (member 1 (ht-keys test-table)) (member "foo" (ht-keys test-table)) (member 2 (ht-values test-table)) (member 3 (ht-values test-table)))))) (ert-deftest ht-test-create () (should (hash-table-p (ht-create)))) (ert-deftest ht-test-set-then-get () (let ((test-table (ht-create))) (ht-set test-table "foo" "bar") (should (equal (ht-get test-table "foo") "bar")))) (ert-deftest ht-test-get-default () (let ((test-table (ht-create))) (should (equal (ht-get test-table "foo" "default") "default")))) (ert-deftest ht-test-get* () (let ((alphabets (ht ("Greek" (ht (1 (ht ('letter "α") ('name "alpha"))) (2 (ht ('letter "β") ('name "beta"))))) ("English" (ht (1 (ht ('letter "a") ('name "A"))) (2 (ht ('letter "b") ('name "B")))))))) ;; Nested (should (equal (ht-get* alphabets "English" 1 'letter) "a")) ;; Non-nested (should (equal (ht-get* (ht (1 "one")) 1) "one")) ;; Base case (no keys) (should (equal (ht-get* alphabets) alphabets)) ;; Works with apply, see #38 (should (equal (apply #'ht-get* (list (ht) 1)) nil)))) (ert-deftest ht-test-setf-ht-get () (let ((test-table (ht (1 "one")))) (should (equal (setf (ht-get test-table 1) "alpha") "alpha"))) (let ((test-table (ht (1 "one") (2 "two")))) (setf (ht-get test-table 1) "alpha") (should (equal (ht-get test-table 1) "alpha")))) (ert-deftest ht-test-setf-ht-get* () (let ((test-table (ht (1 (ht (2 (ht (3 "three")))))))) (should (equal (setf (ht-get* test-table 1 2 3) "gamma") "gamma"))) ;; nested tables { 1 : { 2 : { 3 : three } } } (let ((test-table (ht (1 (ht (2 (ht (3 "three")))))))) (setf (ht-get* test-table 1 2 3) "gamma") (should (equal (ht-get* test-table 1 2 3) "gamma"))) ;; nested tables { 1 : { 2 : two } } (let ((test-table (ht (1 (ht (2 "two")))))) (setf (ht-get* test-table 1 2) "beta") (should (equal (ht-get* test-table 1 2) "beta"))) ;; Non-nested table { 1 : one } (let ((test-table (ht (1 "one")))) (setf (ht-get* test-table 1) "alpha") (should (equal (ht-get* test-table 1) "alpha")))) (ert-deftest ht-test-update () (let ((test-table (ht ("foo" 1)))) (ht-update test-table (ht ("bar" 2))) (should (equal (list "bar" "foo") (sort (ht-keys test-table) 'string<))))) (ert-deftest ht-test-merge () (let ((table1 (ht ("foo" 1))) (table2 (ht ("bar" 2)))) (should (equal (list "bar" "foo") (sort (ht-keys (ht-merge table1 table2)) 'string<))))) (ert-deftest ht-test-create-non-default-test () (let ((test-table (ht-create 'eq))) (should (equal (hash-table-test test-table) 'eq)))) (ert-deftest ht-test-remove () (let ((test-table (ht-create))) (ht-set test-table "foo" "bar") (ht-remove test-table "foo") (should (equal (ht-get test-table "foo") nil)))) (ert-deftest ht-test-clear () (let ((test-table (ht-create))) (ht-set test-table "foo" "bar") (ht-set test-table "biz" "baz") (ht-clear test-table) (should (equal (ht-items test-table) nil)))) (ert-deftest ht-test-keys () (let ((test-table (ht-create))) (ht-set test-table "foo" "bar") (should (equal (ht-keys test-table) (list "foo"))))) (ert-deftest ht-test-values () (let ((test-table (ht-create))) (ht-set test-table "foo" "bar") (should (equal (ht-values test-table) (list "bar"))))) (ert-deftest ht-test-items () (let ((test-table (ht-create))) (ht-set test-table "key1" "value1") (should (equal (ht-items test-table) '(("key1" "value1")))))) (ert-deftest ht-test-map () (let ((total 0)) (ht-map (lambda (key value) (setq total (+ total value))) (ht ("foo" 1) ("bar" 2))) (should (equal total 3)))) (ert-deftest ht-test-map-returns-list () (should (equal (sort (ht-map (lambda (key value) (+ 1 value)) (ht ("foo" 1) ("bar" 2))) '<) (list 2 3)))) (ert-deftest ht-test-amap () (let ((total 0)) (ht-amap (setq total (+ total value)) (ht ("foo" 1) ("bar" 2))) (should (equal total 3)))) (ert-deftest ht-test-each () (let ((total 0)) (ht-each (lambda (key value) (setq total (+ total value))) (ht ("foo" 1) ("bar" 2))) (should (equal total 3)))) (ert-deftest ht-test-aeach () (let ((total 0)) (ht-aeach (setq total (+ total value)) (ht ("foo" 1) ("bar" 2))) (should (equal total 3)))) (ert-deftest ht-test-aeach-nil () "ht-aeach should return nil" (let ((total 0)) (should (equal (ht-aeach (setq total (+ total value)) (ht ("foo" 1) ("bar" 2))) nil)))) (ert-deftest ht-test-select-keys-empty () "ht-select-keys should return an empty table if the keys list is empty" (let ((table (ht (:foo 1) (:bar 3)))) (should (ht-empty? (ht-select-keys table '()))))) (ert-deftest ht-test-select-keys () "size of returned table should be the same as the keys list" (let ((table (ht (:foo 1) (:bar 3)))) (should (equal (ht-size (ht-select-keys table '(:foo :bar))) 2)))) (ert-deftest ht-test-select-keys-not-found () "if the key is not found, it doesn't occur in the returned table" (let ((table (ht (:foo 1) (:bar 3)))) (should (equal (ht-size (ht-select-keys table '(:foo :baz))) 1)))) (ert-deftest ht-test-from-alist () (let* ((alist '(("key1" . "value1"))) (test-table (ht-from-alist alist))) (should (equal (ht-items test-table) '(("key1" "value1")))))) (ert-deftest ht-test-from-alist-masked-values () (let* ((alist '(("key1" . "value1") ("key1" . "value2"))) (test-table (ht-from-alist alist))) (should (equal (ht-items test-table) '(("key1" "value1")))))) (ert-deftest ht-test-from-plist () (let* ((plist '("key1" "value1")) (test-table (ht-from-plist plist))) (should (equal (ht-items test-table) '(("key1" "value1")))))) (ert-deftest ht-test-from-plist-masked-values () (let* ((plist '("key1" "value1" "key2" "value2" "key1" "value3")) (test-table (ht-from-plist plist))) (should (equal (ht-get test-table "key1") "value1")))) (ert-deftest ht-test-to-alist () (let* ((alist '(("key1" . "value1") ("key2" . "value2"))) (test-table (ht-from-alist alist))) (should (or (equal (ht-to-alist test-table) alist) (equal (ht-to-alist test-table) (reverse alist)))))) (ert-deftest ht-test-to-plist () (let* ((test-table (ht-create))) (ht-set test-table "foo" "bar") (should (equal (ht-to-plist test-table) '("foo" "bar"))))) (ert-deftest ht-test-p () "Ensure `ht-p' only returns t for hash-tables." (should (ht-p (ht))) (should-not (ht-p nil))) (ert-deftest ht-test-contains-p () (should (ht-contains-p (ht ("key" nil)) "key")) (should-not (ht-contains-p (ht) "key"))) (ert-deftest ht-test-size () (should (= (ht-size (ht)) 0)) (should (= (ht-size (ht ("foo" "bar"))) 1)) (should (= (ht-size (ht ("foo" "bar") ("baz" "qux"))) 2))) (ert-deftest ht-test-empty () (should (ht-empty? (ht))) (should-not (ht-empty? (ht ("foo" "bar")))) (should-not (ht-empty? (ht ("foo" "bar") ("baz" "qux"))))) (ert-deftest ht-test-select () (let ((results (ht-select (lambda (key value) (= (% value 2) 0)) (ht ("foo" 1) ("bar" 2) ("baz" 3) ("qux" 4))))) (should (= (ht-size results) 2)) (should (= (ht-get results "bar") 2)) (should (= (ht-get results "qux") 4)))) (ert-deftest ht-test-reject () (let ((results (ht-reject (lambda (key value) (= (% value 2) 0)) (ht ("foo" 1) ("bar" 2) ("baz" 3) ("qux" 4))))) (should (= (ht-size results) 2)) (should (= (ht-get results "foo") 1)) (should (= (ht-get results "baz") 3)))) (ert-deftest ht-test-delete-if () (let* ((table (ht ("foo" 1) ("bar" 2) ("baz" 3) ("qux" 4))) (results (ht-delete-if (lambda (key value) (= (% value 2) 0)) table))) (should-not results) (should (= (ht-size table) 2)) (should (= (ht-get table "foo") 1)) (should (= (ht-get table "baz") 3)) (should-not (ht-get table "bar")) (should-not (ht-get table "qux")))) (ert-deftest ht-test-find () (let* ((table (ht ("baz" 3) ("qux" 4))) (result (ht-find (lambda (key value) (= (% value 2) 0)) table))) (should (equal result '("qux" 4))))) (ert-deftest ht-test-find-nil () (let* ((table (ht ("baz" 3) ("qux" 4))) (result (ht-find (lambda (key value) nil) table))) (should (equal result nil)))) (ert-deftest ht-test-equal () ;; Same keys and values. (should (ht-equal-p (ht) (ht))) (should (ht-equal-p (ht (1 2)) (ht (1 2)))) ;; Different values. (should (not (ht-equal-p (ht (1 2)) (ht (1 3))))) ;; Different keys. (should (not (ht-equal-p (ht (1 2)) (ht (2 2))))) ;; Different amount of keys. (should (not (ht-equal-p (ht (1 2)) (ht (1 2) (3 4))))) (should (not (ht-equal-p (ht (1 2) (3 4)) (ht (1 2)))))) (ert-deftest ht-test-two-name-style-predicator () (let ((real-definition (lambda (sym) (let ((def sym)) (while (and def (symbolp def)) (setq def (symbol-function def))) def)))) (mapatoms (lambda (sym) (let ((sym-name (symbol-name sym))) (cond ;; Ignore tests ((string-match-p "\\`ht-test" sym-name) nil) ((string-match "\\`\\(ht.*\\)\\(?:-p\\|\\?\\)\\'" sym-name) (let* ((name-body (match-string 1 sym-name)) (scheme-style-sym (intern (format "%s?" name-body))) (cl-style-sym (intern (format "%s-p" name-body)))) (should (eq (funcall real-definition scheme-style-sym) (funcall real-definition cl-style-sym))))))))))) (defun ht-run-tests () (interactive) (ert-run-tests-interactively "ht-test-")) ht.el-2.3/test/test-helper.el0000644000175000017500000000027714001506323015757 0ustar dogslegdogsleg(require 'f) (defvar ht-test/test-path (f-parent (f-this-file))) (defvar ht-test/root-path (f-parent ht-test/test-path)) (require 'ert) (require 'ht (f-expand "ht" ht-test/root-path)) ht.el-2.3/CHANGELOG.md0000644000175000017500000000564614001506323014040 0ustar dogslegdogsleg## v2.3 ### Features * Added `ht-empty-p` as an alias of `ht-empty?`. * Many functions have been marked as `side-effect-free`, improving performance and byte-compiler warnings. * `ht-get` and `ht-get*` can now be used with `setf`, e.g. ``` (setf (ht-get my-table my-key) new-value) ``` ### Bug Fixes * `ht<-plist` now keeps the first item in the list, if there are duplicates. This makes it consistent with `ht<-alist` and `plist-get`. * `ht-get*` now handles an empty list of keys correctly, and will no longer stack overflow on large lists of keys. * `ht-contains-p` now even supports hash tables that contain the key `ht--not-found`. ## v2.2 * Added `ht-select-keys` * Added `ht-get*` ht.el now uses `defsubst` for many of its functions, which may improve performance in byte-compiled code. ## v2.1 * `ht<-alist` and `ht<-plist` gained an optional argument `test` to specify the equality predicate. * Added `ht-equal?`. ## v2.0 -- API Change Functions names have been changed to be more explicit and consistent. Note that ht.el includes aliases, so v2.0 is fully backwards compatible. Mutation functions now always end with `!`, and `ht-delete-if` has been renamed for consistency with its non-mutating equivalent `ht-reject`. * `ht-set` -> `ht-set!` * `ht-update` -> `ht-update!` * `ht-remove` -> `ht-remove!` * `ht-clear` -> `ht-clear!` * `ht-delete-if` -> `ht-reject!` Predicates now always end with `?`. * `ht-p` -> `ht?` * `ht-contains-p` -> `ht-contains?` Conversion functions now use `<-` and `->`. * `ht-to-alist` -> `ht->alist` * `ht-to-plist` -> `ht->plist` * `ht-from-alist` -> `ht<-alist` * `ht-from-plist` -> `ht<-plist` ## v1.6 * Added `ht-reject` and `ht-select` * Added `ht-delete-if` * Added `ht-find` * Added `ht-empty?` and `ht-size` Also added Travis configuration. ## v1.5 * `ht-aeach` now evaluates to `nil` as it should (use `ht-amap` if you want the resulting hash table). ## v1.4 * Added `ht-merge`. ## v1.3 * Removed runtime dependency on `cl`. ## v1.2 * Fixed various `void-variable` crashes due to `ht-amap` only being declared after its usage. ## v1.1 * Added `ht-contains-p`. ## v1.0 -- API Change * `ht-map` now returns a list, as you'd expect a map function to do. * Added `ht-each` for when you're only interested in side-effects. * Added an anaphoric version of `ht-each`, `ht-aeach`. ## v0.11 * Added `ht-map` and an anaphoric version `ht-amap`. ## v0.10 * Added `ht-p`, an alias of `hash-table-p`, (mainly for completeness). ## v0.9 * Added `ht-update`. ## v0.8 * Added the `ht` macro to make hash table literals easy ## v0.7 * Added `ht-to-alist` and `ht-to-plist` ## v0.6 * Fixed a bug where `ht-from-alist` would overwrite the latest key-value association with older ones ## v0.5 * Added `ht-from-plist` ## v0.4 * Added `ht-from-alist` ## v0.3 * Added ht-copy ## v0.2 * Changed functions from hm-* to ht-* (Emacs doesn't use the term hash map) ## v0.1 * Initial release ht.el-2.3/Cask0000644000175000017500000000020714001506323013017 0ustar dogslegdogsleg(source gnu) (source melpa) (package-file "ht.el") (development (depends-on "f") (depends-on "cl-lib") (depends-on "ert-runner")) ht.el-2.3/ht.el0000644000175000017500000002410614001506323013154 0ustar dogslegdogsleg;;; ht.el --- The missing hash table library for Emacs -*- lexical-binding: t; -*- ;; Copyright (C) 2013 Wilfred Hughes ;; Author: Wilfred Hughes ;; Version: 2.3 ;; Keywords: hash table, hash map, hash ;; Package-Requires: ((dash "2.12.0")) ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; The missing hash table library for Emacs. ;; ;; See documentation at https://github.com/Wilfred/ht.el ;;; Code: (require 'dash) (require 'gv) (eval-when-compile (require 'inline)) (defmacro ht (&rest pairs) "Create a hash table with the key-value pairs given. Keys are compared with `equal'. \(fn (KEY-1 VALUE-1) (KEY-2 VALUE-2) ...)" (let* ((table-symbol (make-symbol "ht-temp")) (assignments (mapcar (lambda (pair) `(ht-set! ,table-symbol ,@pair)) pairs))) `(let ((,table-symbol (ht-create))) ,@assignments ,table-symbol))) (define-inline ht-set! (table key value) "Associate KEY in TABLE with VALUE." (inline-quote (prog1 nil (puthash ,key ,value ,table)))) (defalias 'ht-set 'ht-set!) (define-inline ht-create (&optional test) "Create an empty hash table. TEST indicates the function used to compare the hash keys. Default is `equal'. It can be `eq', `eql', `equal' or a user-supplied test created via `define-hash-table-test'." (declare (side-effect-free t)) (inline-quote (make-hash-table :test (or ,test 'equal)))) (defun ht<-alist (alist &optional test) "Create a hash table with initial values according to ALIST. TEST indicates the function used to compare the hash keys. Default is `equal'. It can be `eq', `eql', `equal' or a user-supplied test created via `define-hash-table-test'." (declare (side-effect-free t)) (let ((h (ht-create test))) ;; the first key-value pair in an alist gets precedence, so we ;; start from the end of the list: (dolist (pair (reverse alist) h) (let ((key (car pair)) (value (cdr pair))) (ht-set! h key value))))) (defalias 'ht-from-alist 'ht<-alist) (defun ht<-plist (plist &optional test) "Create a hash table with initial values according to PLIST. TEST indicates the function used to compare the hash keys. Default is `equal'. It can be `eq', `eql', `equal' or a user-supplied test created via `define-hash-table-test'." (declare (side-effect-free t)) (let ((h (ht-create test))) (dolist (pair (nreverse (-partition 2 plist)) h) (let ((key (car pair)) (value (cadr pair))) (ht-set! h key value))))) (defalias 'ht-from-plist 'ht<-plist) (define-inline ht-get (table key &optional default) "Look up KEY in TABLE, and return the matching value. If KEY isn't present, return DEFAULT (nil if not specified)." (declare (side-effect-free t)) (inline-quote (gethash ,key ,table ,default))) ;; Don't use `ht-set!' here, gv setter was assumed to return the value ;; to be set. (gv-define-setter ht-get (value table key) `(puthash ,key ,value ,table)) (define-inline ht-get* (table &rest keys) "Look up KEYS in nested hash tables, starting with TABLE. The lookup for each key should return another hash table, except for the final key, which may return any value." (declare (side-effect-free t)) (inline-letevals (table keys) (inline-quote (progn (while ,keys (setf ,table (ht-get ,table (pop ,keys)))) ,table)))) (put 'ht-get* 'compiler-macro (lambda (_ table &rest keys) (--reduce-from `(ht-get ,acc ,it) table keys))) (defun ht-update! (table from-table) "Update TABLE according to every key-value pair in FROM-TABLE." (maphash (lambda (key value) (puthash key value table)) from-table) nil) (defalias 'ht-update 'ht-update!) (defun ht-merge (&rest tables) "Crete a new tables that includes all the key-value pairs from TABLES. If multiple have tables have the same key, the value in the last table is used." (let ((merged (ht-create))) (mapc (lambda (table) (ht-update! merged table)) tables) merged)) (define-inline ht-remove! (table key) "Remove KEY from TABLE." (inline-quote (remhash ,key ,table))) (defalias 'ht-remove 'ht-remove!) (define-inline ht-clear! (table) "Remove all keys from TABLE." (inline-quote (prog1 nil (clrhash ,table)))) (defalias 'ht-clear 'ht-clear!) (defun ht-map (function table) "Apply FUNCTION to each key-value pair of TABLE, and make a list of the results. FUNCTION is called with two arguments, KEY and VALUE." (let (results) (maphash (lambda (key value) (push (funcall function key value) results)) table) results)) (defmacro ht-amap (form table) "Anaphoric version of `ht-map'. For every key-value pair in TABLE, evaluate FORM with the variables KEY and VALUE bound. If you don't use both of these variables, then use `ht-map' to avoid warnings." `(ht-map (lambda (key value) ,form) ,table)) (defun ht-keys (table) "Return a list of all the keys in TABLE." (declare (side-effect-free t)) (ht-map (lambda (key _value) key) table)) (defun ht-values (table) "Return a list of all the values in TABLE." (declare (side-effect-free t)) (ht-map (lambda (_key value) value) table)) (defun ht-items (table) "Return a list of two-element lists '(key value) from TABLE." (declare (side-effect-free t)) (ht-amap (list key value) table)) (defalias 'ht-each 'maphash "Apply FUNCTION to each key-value pair of TABLE. Returns nil, used for side-effects only.") (defmacro ht-aeach (form table) "Anaphoric version of `ht-each'. For every key-value pair in TABLE, evaluate FORM with the variables key and value bound." `(ht-each (lambda (key value) ,form) ,table)) (defun ht-select-keys (table keys) "Return a copy of TABLE with only the specified KEYS." (declare (side-effect-free t)) (let (result) (setq result (make-hash-table :test (hash-table-test table))) (dolist (key keys result) (if (not (equal (gethash key table 'key-not-found) 'key-not-found)) (puthash key (gethash key table) result))))) (defun ht->plist (table) "Return a flat list '(key1 value1 key2 value2...) from TABLE. Note that hash tables are unordered, so this cannot be an exact inverse of `ht<-plist'. The following is not guaranteed: \(let ((data '(a b c d))) (equalp data (ht->plist (ht<-plist data))))" (declare (side-effect-free t)) (apply 'append (ht-items table))) (defalias 'ht-to-plist 'ht->plist) (define-inline ht-copy (table) "Return a shallow copy of TABLE (keys and values are shared)." (declare (side-effect-free t)) (inline-quote (copy-hash-table ,table))) (defun ht->alist (table) "Return a list of two-element lists '(key . value) from TABLE. Note that hash tables are unordered, so this cannot be an exact inverse of `ht<-alist'. The following is not guaranteed: \(let ((data '((a . b) (c . d)))) (equalp data (ht->alist (ht<-alist data))))" (declare (side-effect-free t)) (ht-amap (cons key value) table)) (defalias 'ht-to-alist 'ht->alist) (defalias 'ht? 'hash-table-p) (defalias 'ht-p 'hash-table-p) (define-inline ht-contains? (table key) "Return 't if TABLE contains KEY." (declare (side-effect-free t)) (inline-quote (let ((not-found-symbol (make-symbol "ht--not-found"))) (not (eq (ht-get ,table ,key not-found-symbol) not-found-symbol))))) (defalias 'ht-contains-p 'ht-contains?) (define-inline ht-size (table) "Return the actual number of entries in TABLE." (declare (side-effect-free t)) (inline-quote (hash-table-count ,table))) (define-inline ht-empty? (table) "Return true if the actual number of entries in TABLE is zero." (declare (side-effect-free t)) (inline-quote (zerop (ht-size ,table)))) (defalias 'ht-empty-p 'ht-empty?) (defun ht-select (function table) "Return a hash table containing all entries in TABLE for which FUNCTION returns a truthy value. FUNCTION is called with two arguments, KEY and VALUE." (let ((results (ht-create))) (ht-each (lambda (key value) (when (funcall function key value) (ht-set! results key value))) table) results)) (defun ht-reject (function table) "Return a hash table containing all entries in TABLE for which FUNCTION returns a falsy value. FUNCTION is called with two arguments, KEY and VALUE." (let ((results (ht-create))) (ht-each (lambda (key value) (unless (funcall function key value) (ht-set! results key value))) table) results)) (defun ht-reject! (function table) "Delete entries from TABLE for which FUNCTION returns a falsy value. FUNCTION is called with two arguments, KEY and VALUE." (ht-each (lambda (key value) (when (funcall function key value) (remhash key table))) table) nil) (defalias 'ht-delete-if 'ht-reject!) (defun ht-find (function table) "Return (key, value) from TABLE for which FUNCTION returns a truthy value. Return nil otherwise. FUNCTION is called with two arguments, KEY and VALUE." (catch 'break (ht-each (lambda (key value) (when (funcall function key value) (throw 'break (list key value)))) table))) (defun ht-equal? (table1 table2) "Return t if TABLE1 and TABLE2 have the same keys and values. Does not compare equality predicates." (declare (side-effect-free t)) (let ((keys1 (ht-keys table1)) (keys2 (ht-keys table2)) (sentinel (make-symbol "ht-sentinel"))) (and (equal (length keys1) (length keys2)) (--all? (equal (ht-get table1 it) (ht-get table2 it sentinel)) keys1)))) (defalias 'ht-equal-p 'ht-equal?) (provide 'ht) ;;; ht.el ends here ht.el-2.3/Makefile0000644000175000017500000000043014001506323013651 0ustar dogslegdogslegEMACS ?= emacs CASK ?= cask all: test test: clean-elc ${MAKE} unit ${MAKE} compile ${MAKE} unit ${MAKE} clean-elc unit: ${CASK} exec ert-runner compile: ${CASK} exec ${EMACS} -Q -batch -f batch-byte-compile ht.el clean-elc: rm -f ht.elc .PHONY: all test unit compile ht.el-2.3/README.md0000644000175000017500000001410114001506323013470 0ustar dogslegdogsleg# ht.el The missing hash table library for Emacs. [![MELPA](http://melpa.org/packages/ht-badge.svg)](http://melpa.org/#/ht) [![MELPA Stable](http://stable.melpa.org/packages/ht-badge.svg)](http://stable.melpa.org/#/ht) [![Build Status](https://travis-ci.org/Wilfred/ht.el.png?branch=master)](https://travis-ci.org/Wilfred/ht.el) **Table of Contents** - [ht.el](#htel) - [Functions](#functions) - [Return a hash table](#return-a-hash-table) - [Accessing the hash table](#accessing-the-hash-table) - [Mutating the hash table](#mutating-the-hash-table) - [Iterating over the hash table](#iterating-over-the-hash-table) - [Predicates](#predicates) - [Converting from a hash table](#converting-from-a-hash-table) - [Converting to a hash table](#converting-to-a-hash-table) - [Macros](#macros) - [Returning a hash table](#returning-a-hash-table) - [Iterating over the hash table (anaphoric)](#iterating-over-the-hash-table-anaphoric) - [Examples](#examples) - [Why?](#why) - [Similar libraries](#similar-libraries) - [Installation](#installation) - [Changelog](#changelog) - [Running tests](#running-tests) - [What's an alist/plist?](#whats-an-alistplist) ## Functions ### Return a hash table * `ht-create` `(test?)` * `ht-merge` `(&rest tables)` * `ht-copy` `(table)` * `ht-select` `(function table)` * `ht-reject` `(function table)` * `ht-select-keys` `(table keys)` ### Accessing the hash table * `ht-get` `(table key default?)` * `ht-get*` `(table &rest keys)` * `ht-keys` `(table)` * `ht-values` `(table)` * `ht-items` `(table)` * `ht-find` `(function table)` * `ht-size` `(table)` ### Mutating the hash table * `ht-set!` `(table key value)` * `ht-update!` `(table table)` * `ht-remove!` `(table key)` * `ht-clear!` `(table)` * `ht-reject!` `(function table)` ### Iterating over the hash table * `ht-map` `(function table)` * `ht-each` `(function table)` ### Predicates * `ht?` `(table-or-object)` * `ht-contains?` `(table key)` * `ht-equal?` `(table1 table2)` * `ht-empty?` `(table)` ### Converting from a hash table * `ht->alist` `(table)` * `ht->plist` `(table)` ### Converting to a hash table * `ht<-alist` `(alist test?)` * `ht<-plist` `(plist test?)` ## Macros ### Returning a hash table * `ht` `(&rest pairs)` ### Iterating over the hash table (anaphoric) * `ht-amap` `(form table)` * `ht-aeach` `(form table)` ## Examples Creating a hash table and accessing it: ``` emacs-lisp (require 'ht) (defun say-hello (name) (let ((greetings (ht ("Bob" "Hey bob!") ("Chris" "Hi Chris!")))) (ht-get greetings name "Hello stranger!"))) ``` This could be alternatively written as: ``` emacs-lisp (require 'ht) (defun say-hello (name) (let ((greetings (ht-create))) (ht-set! greetings "Bob" "Hey Bob!") (ht-set! greetings "Chris" "Hi Chris!") (ht-get greetings name "Hello stranger!"))) ``` Accessing nested hash tables: ``` emacs-lisp (let ((alphabets (ht ("Greek" (ht (1 (ht ('letter "α") ('name "alpha"))) (2 (ht ('letter "β") ('name "beta"))))) ("English" (ht (1 (ht ('letter "a") ('name "A"))) (2 (ht ('letter "b") ('name "B")))))))) (ht-get* alphabets "Greek" 1 'letter)) ; => "α" ``` `ht-get` and `ht-get*` have gv-setters and so will work with `setf`: ``` emacs-lisp (let ((table (ht-create))) (ht-set! table 1 "A")) ``` is equivalent to ``` emacs-lisp (let ((table (ht-create))) (setf (ht-get table 1) "A")) ``` and ``` emacs-lisp (let ((table (ht (1 (ht (2 (ht (3 "three")))))))) (ht-set! (ht-get (ht-get table 1) 2) 3 :three)) ``` is equivalent to ``` emacs-lisp (let ((table (ht (1 (ht (2 (ht (3 "three")))))))) (setf (ht-get* table 1 2 3) :three)) ``` ## Why? Libraries like [s.el](https://github.com/magnars/s.el) (strings) and [dash.el](https://github.com/magnars/dash.el) (lists) have shown how much nicer Emacs lisp programming can be with good libraries. ht.el aims to similarly simplify working with hash tables. Common operations with hash tables (e.g. enumerate the keys) are too difficult in Emacs lisp. ht.el offers: * A consistent naming scheme (contrast `make-hash-table` with `puthash`) * A more natural argument ordering * Mutation functions always return `nil` * A more comprehensive range of hash table operations, including a conventional map (`ht-map` returns a list, elisp's `maphash` returns nil). ### Similar libraries * [kv.el](https://github.com/nicferrier/emacs-kv) (focuses more on alists) * [mon-hash-utils](http://www.emacswiki.org/emacs/mon-hash-utils.el) ## Installation ht.el is available on [MELPA](https://melpa.org/) (recommended) and [Marmalade](http://marmalade-repo.org/). Add MELPA to your .emacs.d/init.el: ``` emacs-lisp (require 'package) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) ``` then run `M-x package-install ht `. ## Changelog ht.el uses semantic versioning, so an incompatible API change will result in the major version increasing. See [CHANGELOG.md](CHANGELOG.md) for a history of all changes. ## Running tests `M-x ht-run-tests` ## What's an alist/plist? An alist is an association list, which is a list of pairs. It looks like this: ((key1 . value1) (key2 . value2) (key3 . value3)) An alist can also look like this: ((key1 . value1) (key2 . value2) (key1 . oldvalue)) A plist is a property list, which is a flat list with an even number of items. It looks like this: (key1 value1 key2 value2 key3 value3) Both of these are slow. ht.el provides `ht<-alist` and `ht<-plist` to help you convert to hash tables. If you need to work with an alist or plist, use the functions `ht->alist` and `ht->plist` to convert an hash table to those formats. ht.el-2.3/.travis.yml0000644000175000017500000000077014001506323014331 0ustar dogslegdogsleg# Use Trusty as Emacs 26.x is failing on Xenial # https://github.com/rejeep/evm/issues/125 dist: trusty language: generic sudo: false before_install: - curl -fsSkL https://gist.github.com/rejeep/ebcd57c3af83b049833b/raw > x.sh && source ./x.sh - evm install $EVM_EMACS --use --skip - cask env: - EVM_EMACS=emacs-25.1-travis - EVM_EMACS=emacs-25.2-travis - EVM_EMACS=emacs-25.3-travis - EVM_EMACS=emacs-26.1-travis - EVM_EMACS=emacs-26.2-travis script: - emacs --version - make test