This system provides a macro which works similarly to with-slots standard macro, but for hash tables.
The simplest case is when your hash table has symbols as its keys. Macro establishes local names for keys you need and these names are setf-able.
Setfable place is such name or form for which you can apply a setf operation to change its value.
In this example I create an empty hash table and set two keys:
POFTHEDAY> (let ((the-hash (make-hash-table)))
(cl-hash-table-destructuring:with-hash-table-items
(foo bar)
the-hash
;; Now I can use setf on foo and bar
;; to change the content of the-hash
(setf foo "Hello")
(setf bar "World"))
;; Now lets look inside our hash-table:
(maphash (lambda (key value)
(format t "~S -> ~S~%"
key
value))
the-hash))
FOO -> "Hello"
BAR -> "World"
Pay attention, this package contains two similar macroses:
- with-hash-table-items - makes names setf-able by expanding into symbol-macrolet.
- with-hash-table-values - don’t.
Macro with-has-table-items expands into:
(let ((#:keyfn cl-hash-table-destructuring:*keyfn*))
(let ((#:hash-table (the hash-table the-hash)))
(let ((#:foo (funcall #:keyfn 'foo))
(#:bar (funcall #:keyfn 'bar)))
(symbol-macrolet ((foo (gethash #:foo #:hash-table))
(bar (gethash #:bar #:hash-table)))
(setf foo "Hello")
(setf bar "World")))))
Attempt to use setf when using “with-has-table-values” will not issue any errors and will not change the state of the hash, because this macro will expand into:
(let ((#:keyfn cl-hash-table-destructuring:*keyfn*))
(let ((#:hash-table (the hash-table the-hash)))
(let ((foo (gethash (funcall #:keyfn 'foo) #:hash-table))
(bar (gethash (funcall #:keyfn 'bar) #:hash-table)))
(setf foo "Hello")
(setf bar "World"))))
Of couse, not all hash tables has only symbols in their keys. If your keys have other types, then there are two ways to use “cl-hash-table-destructuring”.
You can give it key’s value explicitly like you can do when using “with-slots” with custom slot names.
In this example, we use strings as keys:
POFTHEDAY> (let ((the-hash (make-hash-table :test 'equal)))
(cl-hash-table-destructuring:with-hash-table-items
((foo "Foo") (bar "Bar"))
the-hash
;; Now I can use setf on foo and bar
;; to change the content of the-hash
(setf foo "Hello")
(setf bar "World"))
;; Now lets look inside our hash-table:
(maphash (lambda (key value)
(format t "~S -> ~S~%"
key
value))
the-hash))
"Foo" -> "Hello"
"Bar" -> "World"
Or, you can provide a function to convert symbols into the keys:
In this example, we will use a function to make keys with symbols converted to pascal-styled strings.
POFTHEDAY> (let ((the-hash (make-hash-table :test 'equal)))
(cl-hash-table-destructuring:with-hash-table-items-fn
(some-key-name)
(the-hash (lambda (symbol)
(symbol-name (kebab:to-pascal-case symbol))))
;; Now I can use setf on foo and bar
;; to change the content of the-hash
(setf some-key-name "Hello World!"))
;; Now lets look inside our hash-table:
(maphash (lambda (key value)
(format t "~S -> ~S~%"
key
value))
the-hash))
"SomeKeyName" -> "Hello World!"
If you are interested in such thing as working with nested data structures, then you might also be interested in these Common Lisp systems: