a.el: Emacs Lisp Functions for Associative Data Structures

0
3743

The a.el Emacs Lisp library contains functions for handling associative lists and hash tables. It provides an API in a consistent and functional way, which is greatly influenced by Clojure, dash.el and seq.el. The package is written by Arne Brasseur.

The latest tagged release of the a.el Emacs Lisp library is v0.1.1. You can use association lists, or hash tables and even vectors with certain functions. The implemented functions are pure and hence do not mutate objects. The library is released under the GNU General Public License v3.0 and it requires Emacs version 25 or later.

You can include the a.el source file to your Emacs load path and use the following Emacs Lisp code snippet to load it on to the existing running environment:

(load-file “/path/to/a.el”)

Usage
We shall now explore the various functions provided by a.el using examples for both associative lists and hash tables.
You can define an associative list using the a-list function, and a hash table using the a-hash-table function. A few examples are given below to illustrate their usage:

(a-list key-value-pairs) ;; Syntax

(a-list :foo 1 :bar 2)
((:foo . 1) (:bar . 2))

(setq x (a-list :foo 1 :bar 2))
((:foo . 1) (:bar . 2))

(a-hash-table key-value-pairs) ;; Syntax

(setq h (a-hash-table :a 1 :b 2))
(:a 1 :b 2)

The a-associative function will return true if the object passed to it is either an associative list or hash table, and nil otherwise. For example:

(a-associative? object) ;; Syntax

(a-associative? x)
t
(a-associative? h)
t

(a-associative? :foo)
nil
(a-associative? ‘(1 2 3))
nil

The Emacs Lisp source code for the a-associative function is provided below:

(defun a-associative-p (obj)
(or (not obj)
(hash-table-p obj)
(and (consp obj) (consp (car obj)))))

(defalias ‘a-associative? ‘a-associative-p)

You can retrieve a value for a specific key using the ‘a-get’ function as shown below:

(a-get map key) ;; Syntax

(a-get x :foo)
1
(a-get x :bar)
2
(a-get h :a)
1
(a-get h :notexist)
nil

The keys in the associative list or hash table can be obtained using the a-keys function, while the values can be retrieved using the a-vals function, as illustrated below:

(a-keys collection) ;; Syntax

(a-keys x)
(:foo :bar)

(a-keys h)
(:a :b)

(a-vals collection) ;; Syntax

(a-vals x)
(1 2)

(a-vals h)
(1 2)

All the functions in a.el provide implementation for both associative lists and hash tables. As an example, the a-keys function definition is given below:

(defun a-keys (coll)
“Return the keys in the collection COLL.”
(cond
((listp coll)
(mapcar #’car coll))

((hash-table-p coll)
(hash-table-keys coll))))

You can check if two collections are the same using the a-equal function. A few examples are as follows:

(a-equal collectionA collectionB) ;; Syntax

(a-equal x (a-list :alpha 1 :bar 2))
nil
(a-equal x (a-list :foo 1 :bar 2))
t

(a-equal h (a-hash-table :a 1 :b 2))
t

The a-has-key? API takes two arguments – a collection and a key, and returns true if the key is present and nil otherwise:

(a-has-key? collection key) ;; Syntax
(a-has-key? x :foo)
t
(a-has-key? x :gamma)
nil

(a-has-key? h :a)
t

(a-has-key? h :foo)
nil

You can count the number of key-value pairs in a collection using the a-count function, as shown below:

(a-count collection) ;; Syntax

(a-count x)
2

(a-count h)
2

The source code of the a-count function is as follows:

(defun a-count (coll)

“Count the number of key-value pairs in COLL.

Like length, but can also return the length of hash tables.”

(cond
((seqp coll)
(length coll))

((hash-table-p coll)
(hash-table-count coll))))

The a-assoc function takes a collection and key-value pairs, and returns an updated collection with the associated values. A couple of examples are shown below:

(a-assoc collection key-value-pairs) ;; Syntax

(a-assoc x :gamma 3)
((:gamma . 3) (:foo . 1) (:bar . 2))

(a-assoc h :c 3)
(:a 1 :b 2 :c 3)

The a-dissoc function, on the other hand, removes the keys passed as an argument to it and returns a new collection. Examples for the associative list and hash tables are as follows:

(a-dissoc collection keys) ;; Syntax

(a-dissoc x :bar)
((:foo . 1))

(a-dissoc h :b)
(:a 1)

You can associate new values in a collection for specific keys using the a-assoc-in function, as illustrated below:

(a-assoc-in collection keys value) ;;

Syntax

(a-assoc-in x [:foo] 10)
((:foo . 10) (:bar . 2))

(a-assoc-in h [:a] 100)
(:a 100 :b 2)

The a-merge function allows you to merge multiple associative collections. The return type is the type of the first collection. A couple of examples are provided below:

(a-merge collections) ;; Syntax

(a-merge x (a-list :gamma 5))
((:gamma . 5) (:foo . 1) (:bar . 2))

(a-merge h (a-hash-table :c 3))
(:a 1 :b 2 :c 3)

You can apply a function while merging multiple collections using the a-merge-with function. In the following associative list example, the initial value for the key foo is 1, and its value is incremented by 3. For the hash-table example, the initial value for b is 2 and is incremented by 1.

(a-merge-with function collections) ;; Syntax

(a-merge-with ‘+ x (a-list :foo 3))
((:foo . 4) (:bar . 2))

(a-merge-with ‘+ h (a-hash-table :b 1))
(:a 1 :b 3)

The a-update function takes four arguments — a collection, a key, a function and additional arguments to the function. It applies the function with its arguments to the specified key and returns a new collection. A couple of examples are provided below:

(a-update collection key function arguments) ;; Syntax

(a-update (a-list :name “Mr. “) :name ‘concat “Jack”)
((:name . “Mr. Jack”))

(a-update (a-hash-table :name “The Big “) :name ‘concat “Bang Theory”)
(:name “The Big Bang Theory”)

The a-update-in function is similar to the a-update function, except that it can update a value in a nested collection. An example each for an association list and a hash table is given below:

(a-update-in collection keys function arguments) ;; Syntax

(a-update-in x [:foo] ‘+ 1)
((:foo . 2) (:bar . 2))

(a-update-in h [:a] ‘+ 1)
(:a 2 :b 2)

Tests
The a.el project contains tests that you can run with Cask and ert-runner. You need to first clone the source code repository using the following command:

$ git clone https://github.com/plexus/a.el
Cloning into ‘a.el’...
remote: Enumerating objects: 118, done.
remote: Total 118 (delta 0), reused 0 (delta 0), pack-reused 118
Receiving objects: 100% (118/118), 44.86 KiB | 740.00 KiB/s, done.
Resolving deltas: 100% (62/62), done.

If you do not have Cask, install the same using the instructions provided in its README file at https://github.com/cask/cask.

You can then change directory into the cloned a.el folder and run cask install. This will locally install the required dependencies for running the tests.
You can now simply run cask exec ert-runner from the top-level sources directory to execute the tests as shown below:

$ cask exec ert-runner
Running tests on Emacs 26.3
Loading /tmp/a.el/a.el (source)...
.................
Ran 17 tests in 0.003 seconds

You are encouraged to read the source code at https://github.com/plexus/a.el/blob/master/a.el to know more about the available functions provided by a.el.

LEAVE A REPLY

Please enter your comment!
Please enter your name here