Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time


The missing hash table library for Emacs.

MELPA MELPA Stable Build Status

Table of Contents


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)


  • 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?)


Returning a hash table

  • ht (&rest pairs)

Iterating over the hash table (anaphoric)

  • ht-amap (form table)
  • ht-aeach (form table)


Creating a hash table and accessing it:

(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:

(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:

(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:

(let ((table (ht-create)))
  (ht-set! table 1 "A"))

is equivalent to

(let ((table (ht-create)))
  (setf (ht-get table 1) "A"))


(let ((table (ht (1 (ht (2 (ht (3 "three"))))))))
  (ht-set! (ht-get (ht-get table 1) 2) 3 :three))

is equivalent to

(let ((table (ht (1 (ht (2 (ht (3 "three"))))))))
  (setf (ht-get* table 1 2 3) :three))


Libraries like s.el (strings) and 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


ht.el is available on MELPA (recommended) and Marmalade.

Add MELPA to your .emacs.d/init.el:

(require 'package)
(add-to-list 'package-archives '("melpa" . "") t)

then run M-x package-install <RET> ht <RET>.


ht.el uses semantic versioning, so an incompatible API change will result in the major version increasing. See 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.