map.el vs. other libraries through hundreds of examples
If you code in Emacs Lisp, you'll deal with alists, plists, hash tables, and other kinds of key–value collections — also known as maps.
Today we're going to compare map-manipulation functions. Our focus will be on a few notable places where you can find them:
primitive
(C source code) (copy-alist
,plist-put
,gethash
...)xht.el
map.el
ht.el
The reasons why you'd prefer one library over another is a broad topic. Our scope here is narrower: to see them in action.
map.el
This article is centered on map.el
. This is for three reasons:
- It's a native library that has grown in user adoption.
- It has over the years added more functions, which can deal with alists, plists, hash tables, and arrays.
- Last, but not least, because I never seem to use it.
So what do I use instead?
More broadly, what else would produce the same results as map
?
To answer these questions, I:
- listed
map.el
's functions, - collected usage examples of each, sometimes adding examples of my own,
- and, for each
map
example, answered: what alternatives do I see?
That's what you'll find below. We'll look at solutions side by side to get a sense of how they compare.
map.el's functions and their alternatives
A summary of where the functions come from:
;;; Libraries used here ;;;; map (native) (map-foo x y) ; map ;;;; other (non-native) (-foo x y) ; dash (--foo x y) ; dash (->> x y z) ; dash (-> x y z) ; dash (h-foo x y) ; xht (h<-foo x) ; xht (h= x y z) ; xht (ht-foo x y) ; ht (ht<-foo x) ; ht (##foo % %2) ; llama ;;;; all else (native) (let-alist a x) ; let-alist (foo else) ; C source code, subr, or subr-x
For each map
function:
- I pick one or more examples, always showing a
map
example first. - This is followed by one or more alternative solutions.
- These alternatives will almost always be sorted from largest to shortest.
Let's see them.
Map Predicates
mapp
Although xht
doesn't have this exact predicate (yet?), it can be composed with just two functions.
;;; some non-map thing ;;;; symbol (mapp 'something) => nil (keywordp (h-type 'something)) => nil ;;;; number (mapp 42) => nil (keywordp (h-type 42)) => nil
;;; hash table ;;;; general (mapp #s(hash-table data (a 1 b 2))) => t (keywordp (h-type (h* 'a 1 'b 2))) => t ;;;; specific for hash tables (hash-table-p #s(hash-table data (a 1 b 2))) => t ; map (ht-p (ht ('a 1) ('b 2))) => t ; ht (h? (h* 'a 1 'b 2)) => t ; xht
;;; vector ;;;; general (mapp [a b c]) => t (keywordp (h-type [a b c])) => t ;;;; specific for vectors (vectorp [a b c]) => t
;;; alist ;;;; general (mapp '((a . 1) (b . 2))) => t (keywordp (h-type '((a . 1) (b . 2)))) => t ;;;; specific for alists (h-alist? '((a . 1) (b . 2))) => t
;;; plist ;;;; general (mapp '(a 1 b 2)) => t (keywordp (h-type '(a 1 b 2))) => t ;;;; specific for plists (h-plist? '(a 1 b 2)) => t (plistp '(a 1 b 2)) => t
Is '(a b c) a map? If so, of what type?
Good question. It depends on whom you ask.
;;;; map.el says yes, it's a map (mapp '(a b c)) => t
;;;; map.el parses it as a plist whose last value is implicitly nil (map-pairs '(a b c)) => '((a . b) (c))
;;;; subr.el, json.el, and xht.el disagree (plistp '(a b c)) => nil (json-plist-p '(a b c)) => nil (h-plist? '(a b c)) => nil ;;;; For them, plists must have an even number of items (defun plistp (object) "Non-nil if and only if OBJECT is a valid plist." (let ((len (proper-list-p object))) (and len (zerop (% len 2))))) ;; ^^^^^^^^^^^^^^
;;;; json.el is even stricter: besides accepting no implicit trailing nil value... (json-plist-p '(:a 1 :b)) => nil ;;;; ...keys must also be keywords (json-plist-p '(a 1 b 2)) => nil (json-plist-p '(:a 1 :b 2)) => t
;;;; To further complicate it, other native functions do treat it as plist (plist-member '(a b c) 'd) => nil ; ok, 'd is not there, not a key (plist-member '(a b c) 'b) => nil ; ok, 'b is a value, not a key (plist-member '(a b c) 'a) => '(a b c) ; ok, 'a is a key (plist-member '(a b c) 'c) => '(c) ; well, 'c is a key!
;;;; Both xht and ht accept an explicit conversion by discarding the odd one (h<-plist '(a b c)) H=> (h* 'a 'b) (ht<-plist '(a b c)) H=> (h* 'a 'b)
;;;; Nevertheless, xht actually identifies it as a simple list... (h-type '(a b c)) => :list ;;;; ...and in a DWIM conversion takes its indices as keys... (h<-it '(a b c)) H=> (h* 0 'a 1 'b 2 'c) ;;;; ...which is exactly what it would do with the corresponding vector (h<-it [a b c]) H=> (h* 0 'a 1 'b 2 'c)
;;;; On the other hand, the list below would be considered a plist... (plistp '(a b c d)) => t (h-plist? '(a b c d)) => t (h-type '(a b c d)) => :plist ;;;; ...and converted accordingly (h<-it '(a b c d)) H=> (h* 'a 'b 'c 'd)
;;;; So we have this situation (h<-it [a b c d]) H=> (h* 0 'a 1 'b 2 'c 3 'd) (map-into [a b c d] 'hash-table) H=> (h* 0 'a 1 'b 2 'c 3 'd) (h<-it '(a b c d)) H=> (h* 'a 'b 'c 'd) (map-into '(a b c d) 'hash-table) H=> (h* 'a 'b 'c 'd) (h<-it [a b c]) H=> (h* 0 'a 1 'b 2 'c) (map-into [a b c] 'hash-table) H=> (h* 0 'a 1 'b 2 'c) (h<-it '(a b c)) H=> (h* 0 'a 1 'b 2 'c) ; analogy with [a b c] (map-into '(a b c) 'hash-table) H=> (h* 'a 'b 'c nil) ; analogy with '(a b c d)
For map.el
, it seems that we have:
Is this-list an alist? No? Then it's a plist.
For xht.el
, it looks more like:
Is
type-of
this-list the symbol'cons
? Yes?
Then is it a cons pair? No?
Is it a list of lists? No?
An alist, then? No?
Perhaps a plist — does it have an even number of items? Also no?
Well, then we'll treat it as a generic simple list.
Here's the relevant part from h-type
that does that:
('cons (cond ((-cons-pair-p obj) :cons-pair) ((h-lol? obj) :lol) ((h-alist? obj) :alist) ((h-plist? obj) :plist) (t :list)))
Different designs.
So what to do in xht
when you find yourself converting an ambiguous input type?
;;;; Do What I Mean (h<-it '(a b c d)) H=> (h* 'a 'b 'c 'd) ; "even number, most likely a plist..." (h<-it '(a b c)) H=> (h* 0 'a 1 'b 2 'c) ; "odd number, probably not a plist..."
Answer: consider using an exact conversion function instead.
;;;; Do What I Say (h<-plist '(a b c d)) H=> (h* 'a 'b 'c 'd) (h<-plist '(a b c)) H=> (h* 'a 'b) (h<-list '(a b c d)) H=> (h* 0 'a 1 'b 2 'c 3 'd) (h<-list '(a b c)) H=> (h* 0 'a 1 'b 2 'c)
Another mismatch
;;;; simple string ;;;;; map.el autodetects a vector (mapp "just a\nstring") => t (map-keys "just a\nstring") => '(0 1 2 3 4 5 6 7 8 9 10 11 12) (map-values "just a\nstring") => '(106 117 115 116 32 97 10 115 116 114 105 110 103) (concat (map-values "just a\nstring")) => "just a\nstring" ;;;;; whereas xht.el autodetects lines of string... (keywordp (h-type "just a\nstring")) => t (h-type "just a\nstring") => :lines (-> "just a\nstring" h<-it h-keys) => '(0 1) (-> "just a\nstring" h<-it h-values) => '("just a" "string") ;;;;; ...but could also treat it as vector (-> "just a\nstring" h<-vector h-keys) => '(0 1 2 3 4 5 6 7 8 9 10 11 12) (-> "just a\nstring" h<-vector h-values) => '(106 117 115 116 32 97 10 115 116 114 105 110 103) (-> "just a\nstring" h<-vector h-values concat) => "just a\nstring"
map-contains-key
;;; hash table (map-contains-key #s(hash-table data (x 1 y 2)) 'y) => t ; map (and (gethash 'y #s(hash-table data (x 1 y 2))) t) => t ; C src (ht-contains? (ht ('x 1) ('y 2)) 'y) => t ; ht (h-has-key? (h* 'x 1 'y 2) 'y) => t ; xht
;;; alist (map-contains-key '((x . 1) (y . 2)) 'y) => t ; map (ht-contains? (ht<-alist '((x . 1) (y . 2))) 'y) => t ; ht (h-has-key? (h<-alist '((x . 1) (y . 2))) 'y) => t ; xht (and (alist-get 'y '((x . 1) (y . 2))) t) => t ; C src
;;; plist (and (map-contains-key '(:x 1 :y 2) :y) t) => t ; map (ht-contains? (ht<-plist '(:x 1 :y 2)) :y) => t ; ht (h-has-key? (h<-plist '(:x 1 :y 2)) :y) => t ; xht (and (plist-get '(:x 1 :y 2) :y) t) => t ; C src
;;; vector (map-contains-key [x y] 1) => t ; map (h-has-key? (h<-vector [x y]) 1) => t ; xht (> (length [x y]) 1) => t ; C src
map-empty-p
;;; hash table (map-empty-p #s(hash-table data ())) => t ; map (= 0 (hash-table-count #s(hash-table data ()))) => t ; C src (hash-table-empty-p #s(hash-table data ())) => t ; subr-x (= 0 (h-length (h*))) => t ; xht (= 0 (ht-size (ht))) => t ; ht (ht-empty? (ht)) => t ; ht (h-empty? (h*)) => t ; xht
;;; alist (map-empty-p ()) => t ; map (= 0 (ht-size (ht<-alist ()))) => t ; ht (= 0 (h-length (h<-it ()))) => t ; xht (ht-empty? (ht<-alist ())) => t ; ht (h-empty? (h<-it ())) => t ; xht (= 0 (length ())) => t ; C src (h-it-empty? ()) => t ; xht
;;; vector (map-empty-p []) => t ; map (= 0 (h-length (h<-it []))) => t ; xht (h-empty? (h<-it [])) => t ; xht (= 0 (length [])) => t ; C src (h-it-empty? []) => t ; xht
map-every-p
Note that both xht
and ht
already require dash
.
So I won't add + dash
to any solutions that use either of these two libraries, even if some additional dash function is used.
;;; alist (map-every-p (lambda (k v) (= (* k k) v)) '((3 . 9) (4 . 16))) => t ; map (not (h-any (-not (lambda (k v) (= (* k k) v))) (h<-it '((3 . 9) (4 . 16))))) => t ; xht (h-empty? (h-rej (lambda (k v) (= (* k k) v)) (h<-it '((3 . 9) (4 . 16))))) => t ; xht (not (h--any (not (= (* key key) value)) (h<-it '((3 . 9) (4 . 16))))) => t ; xht (h-empty? (h--rej (= (* key key) value) (h<-it '((3 . 9) (4 . 16))))) => t ; xht (not (h-any (-not (##= (* % %) %2)) (h<-it '((3 . 9) (4 . 16))))) => t ; xht + llama (h-all? (lambda (k v) (= (* k k) v)) (h<-it '((3 . 9) (4 . 16)))) => t ; xht (h-all (lambda (k v) (= (* k k) v)) (h<-it '((3 . 9) (4 . 16)))) => t ; xht (h-empty? (h-rej (##= (* % %) %2) (h<-it '((3 . 9) (4 . 16))))) => t ; xht + llama (-every (-lambda ((k . v)) (= (* k k) v)) '((3 . 9) (4 . 16))) => t ; dash (-all? (-lambda ((k . v)) (= (* k k) v)) '((3 . 9) (4 . 16))) => t ; dash (--every (= (expt (car it) 2) (cdr it)) '((3 . 9) (4 . 16))) => t ; dash (--all? (= (expt (car it) 2) (cdr it)) '((3 . 9) (4 . 16))) => t ; dash (h--all? (= (* key key) value) (h<-it '((3 . 9) (4 . 16)))) => t ; xht (h--all (= (* key key) value) (h<-it '((3 . 9) (4 . 16)))) => t ; xht (h-all? (##= (* % %) %2) (h<-it '((3 . 9) (4 . 16)))) => t ; xht + llama (h-all (##= (* % %) %2) (h<-it '((3 . 9) (4 . 16)))) => t ; xht + llama (map-every-p (##= (* % %) %2) '((3 . 9) (4 . 16))) => t ; map + llama
(Note that all
and all?
(in xht
), and every
and all?
(in dash
), be it regular or anaphoric, only return the same result here because the predicate used in the example above is #'=
, which returns nil
or t
.)
Now, if we were dealing with hash tables:
;;; hash table (map-every-p (lambda (k v) (= (* k k) v)) #s(hash-table data (3 9 4 16))) => t ; map (not (h-any (-not (lambda (k v) (= (* k k) v))) (h* 3 9 4 16))) => t ; xht (map-every-p (##= (* % %) %2) #s(hash-table data (3 9 4 16))) => t ; map + llama (h-empty? (h-rej (lambda (k v) (= (* k k) v)) (h* 3 9 4 16))) => t ; xht (not (h--any (not (= (* key key) value)) (h* 3 9 4 16))) => t ; xht (h-empty? (h--rej (= (* key key) value) (h* 3 9 4 16))) => t ; xht (not (h-any (-not (##= (* % %) %2)) (h* 3 9 4 16))) => t ; xht + llama (h-all? (lambda (k v) (= (* k k) v)) (h* 3 9 4 16)) => t ; xht (h-all (lambda (k v) (= (* k k) v)) (h* 3 9 4 16)) => t ; xht (h-empty? (h-rej (##= (* % %) %2) (h* 3 9 4 16))) => t ; xht + llama (h--all? (= (* key key) value) (h* 3 9 4 16)) => t ; xht (h--all (= (* key key) value) (h* 3 9 4 16)) => t ; xht (h-all? (##= (* % %) %2) (h* 3 9 4 16)) => t ; xht + llama (h-all (##= (* % %) %2) (h* 3 9 4 16)) => t ; xht + llama
Note that although the combination of llama + h-all
would offer the shortest overall solution here, it wouldn't be quite as readable as the two pure xht
anaphorics above it, in both of whose forms the arguments being passed — key
and value
— are more immediately obvious.
Likewise for llama + h-any
in the map-some
examples below.
map-some
With "regular" boolean predicates:
;;; alist (map-some (lambda (k v) (= (* k k) v)) '((3 . 9) (4 . 12))) => t ; map (not (h-empty? (h-sel (lambda (k v) (= (* k k) v)) (h<-it '((3 . 9) (4 . 12)))))) => t ; xht (not (h-empty? (h--sel (= (* key key) value) (h<-it '((3 . 9) (4 . 12)))))) => t ; xht (not (h-empty? (h-sel (##= (* % %) %2) (h<-it '((3 . 9) (4 . 12)))))) => t ; xht + llama (h-any? (lambda (k v) (= (* k k) v)) (h<-it '((3 . 9) (4 . 12)))) => t ; xht (h-any (lambda (k v) (= (* k k) v)) (h<-it '((3 . 9) (4 . 12)))) => t ; xht (-any? (-lambda ((k . v)) (= (* k k) v)) '((3 . 9) (4 . 12))) => t ; dash (-any (-lambda ((k . v)) (= (* k k) v)) '((3 . 9) (4 . 12))) => t ; dash (--any? (= (expt (car it) 2) (cdr it)) '((3 . 9) (4 . 12))) => t ; dash (h--any? (= (* key key) value) (h<-it '((3 . 9) (4 . 12)))) => t ; xht (--any (= (expt (car it) 2) (cdr it)) '((3 . 9) (4 . 12))) => t ; dash (h--any (= (* key key) value) (h<-it '((3 . 9) (4 . 12)))) => t ; xht (h-any? (##= (* % %) %2) (h<-it '((3 . 9) (4 . 12)))) => t ; xht + llama (h-any (##= (* % %) %2) (h<-it '((3 . 9) (4 . 12)))) => t ; xht + llama (map-some (##= (* % %) %2) '((3 . 9) (4 . 12))) => t ; map + llama
(Note that any
and any?
(in both xht
and dash
), be it regular or anaphoric, only return the same result because the predicate used in the examples above is #'=
, which returns nil
or t
.)
;;; hash table (map-some (lambda (k v) (= (* k k) v)) #s(hash-table data (3 9 4 12))) => t ; map (not (h-empty? (h-sel (lambda (k v) (= (* k k) v)) (h* 3 9 4 12)))) => t ; xht (not (h-empty? (h--sel (= (* key key) value) (h* 3 9 4 12)))) => t ; xht (map-some (##= (* % %) %2) #s(hash-table data (3 9 4 12))) => t ; map + llama (not (h-empty? (h-sel (##= (* % %) %2) (h* 3 9 4 12)))) => t ; xht + llama (h-any? (lambda (k v) (= (* k k) v)) (h* 3 9 4 12)) => t ; xht (h-any (lambda (k v) (= (* k k) v)) (h* 3 9 4 12)) => t ; xht (h--any? (= (* key key) value) (h* 3 9 4 12)) => t ; xht (h--any (= (* key key) value) (h* 3 9 4 12)) => t ; xht (h-any? (##= (* % %) %2) (h* 3 9 4 12)) => t ; xht + llama (h-any (##= (* % %) %2) (h* 3 9 4 12)) => t ; xht + llama
With a predicate whose non-nil result is not t
:
;;; hash table (map-some #'string-match-p #s(hash-table data ("x" "quux" "a" "bar"))) => 3 ; map (h-any #'string-match-p (h* "x" "quux" "a" "bar")) => 3 ; xht
;;; hash table (map-some (lambda (k v) (string-match-p v k)) #s(hash-table data ("quux" "x" "bar" "a"))) ; map (map-some (##string-match-p %2 %1) #s(hash-table data ("quux" "x" "bar" "a"))) ; map + llama (map-some (-flip #'string-match-p) #s(hash-table data ("quux" "x" "bar" "a"))) ; map + dash (h-any (lambda (k v) (string-match-p v k)) (h* "quux" "x" "bar" "a")) ; xht (h--any (string-match-p value key) (h* "quux" "x" "bar" "a")) ; xht (h-any (##string-match-p %2 %1) (h* "quux" "x" "bar" "a")) ; xht + llama (h-any (-flip #'string-match-p) (h* "quux" "x" "bar" "a")) ; xht => 3
Building Maps
map-copy
;;; hash table (h= (map-copy #s(hash-table data (a 1 b 2))) ; map (copy-hash-table #s(hash-table data (a 1 b 2))) ; C src (ht-copy (ht ('a 1) ('b 2))) ; ht (h-copy (h* 'a 1 'b 2))) ; xht => t
;;; alist (map-copy '((a . 1) (b . 2))) => '((a . 1) (b . 2)) ; map (copy-alist '((a . 1) (b . 2))) => '((a . 1) (b . 2)) ; C src
;;; plist (map-copy '(a 1 b 2)) => '(a 1 b 2) ; map (copy-sequence '(a 1 b 2)) => '(a 1 b 2) ; C src (-copy '(a 1 b 2)) => '(a 1 b 2) ; dash
;;; vector (map-copy [a b c]) => [a b c] ; map (copy-sequence [a b c]) => [a b c] ; C src (-copy [a b c]) => [a b c] ; dash
map-into
(map-into #s(hash-table data (x 1 y 2)) 'list) => '((x . 1) (y . 2)) ; map (nreverse (ht->alist (ht ('x 1) ('y 2)))) => '((x . 1) (y . 2)) ; ht (h->alist (h* 'x 1 'y 2)) => '((x . 1) (y . 2)) ; xht
map-pairs
;;; hash table (map-pairs #s(hash-table data (x 1 y 2))) => '((x . 1) (y . 2)) ; map (nreverse (ht->alist (ht ('x 1) ('y 2)))) => '((x . 1) (y . 2)) ; ht (h->alist (h* 'x 1 'y 2)) => '((x . 1) (y . 2)) ; xht
;;; plist (map-pairs '(x 1 y 2)) => '((x . 1) (y . 2)) ; map (nreverse (ht->alist (h<-plist '(x 1 y 2)))) => '((x . 1) (y . 2)) ; ht (h->alist (h<-it '(x 1 y 2))) => '((x . 1) (y . 2)) ; xht
;;; vector (map-pairs [a b c]) => '((0 . a) (1 . b) (2 . c)) ; map (h->alist (h<-it [a b c])) => '((0 . a) (1 . b) (2 . c)) ; xht
Utility Functions
map-length
(Note: map.el
has an open question under map-length
's definition:
(cl-defgeneric map-length (map) ;; FIXME: Should we rename this to `map-size'? …)
I'd argue that no, map.el
should keep it as map-length
.
Why? For the same reasons that led xht to choose h-length.)
;;; hash table (map-length #s(hash-table data (a 1 b 2 c 3))) => 3 ; map (hash-table-count #s(hash-table data (a 1 b 2 c 3))) => 3 ; C src (ht-size (ht ('a 1) ('b 2) ('c 3))) => 3 ; ht (h-length (h* 'a 1 'b 2 'c 3)) => 3 ; xht
;;; alist (map-length '((a . 1) (b . 2) (c . 3))) => 3 ; map (ht-size (ht<-alist '((a . 1) (b . 2) (c . 3)))) => 3 ; ht (h-length (h<-it '((a . 1) (b . 2) (c . 3)))) => 3 ; xht (length '((a . 1) (b . 2) (c . 3))) => 3 ; C src
;;; plist (map-length '(:a 1 :b 2 :c 3)) => 3 ; map (ht-size (ht<-plist '(:a 1 :b 2 :c 3))) => 3 ; ht (h-length (h<-it '(:a 1 :b 2 :c 3))) => 3 ; xht (/ (length '(:a 1 :b 2 :c 3)) 2) => 3 ; C src
;;; vector (map-length [a b c]) => 3 ; map (h-length (h<-it [a b c])) => 3 ; xht (length [a b c]) => 3 ; C src
map-elt
;;; hash table (map-elt #s(hash-table data (x 1 y 2)) 'y) => 2 ; map (-let [(&hash 'y) #s(hash-table data (x 1 y 2))] y) => 2 ; dash (gethash 'y #s(hash-table data (x 1 y 2))) => 2 ; C src (-let [(&hash 'y) (h* 'x 1 'y 2)] y) => 2 ; dash + xht (ht-get (ht ('x 1) ('y 2)) 'y) => 2 ; ht (h-get (h* 'x 1 'y 2) 'y) => 2 ; xht (h-let (h* 'x 1 'y 2) .y) => 2 ; xht
;;; alist (map-elt '((x . 1) (y . 2)) 'y) => 2 ; map (ht-get (ht<-alist '((x . 1) (y . 2))) 'y) => 2 ; ht (-let [(&alist 'y) '((x . 1) (y . 2))] y) => 2 ; dash (h-get (h<-it '((x . 1) (y . 2))) 'y) => 2 ; xht (h-let (h<-it '((x . 1) (y . 2))) .y) => 2 ; xht (alist-get 'y '((x . 1) (y . 2))) => 2 ; subr (let-alist '((x . 1) (y . 2)) .y) => 2 ; let-alist (h-let-it '((x . 1) (y . 2)) .y) => 2 ; xht
;;; plist (map-elt '(x 1 y 2) 'y) => 2 ; map (ht-get (ht<-plist '(x 1 y 2)) 'y) => 2 ; ht (-let [(&plist 'y) '(x 1 y 2)] y) => 2 ; dash (h-get (h<-it '(x 1 y 2)) 'y) => 2 ; xht (h-let (h<-it '(x 1 y 2)) .y) => 2 ; xht (plist-get '(x 1 y 2) 'y) => 2 ; C src (h-let-it '(x 1 y 2) .y) => 2 ; xht
;;; vector (map-elt [a b c] 1) => 'b ; map (h-get (h<-it [a b c]) 1) => 'b ; xht (h-let-it [a b c] \.1) => 'b ; xht (aref [a b c] 1) => 'b ; C src (elt [a b c] 1) => 'b ; C src
map-nested-elt
;;; nested hash table (map-nested-elt #s(hash-table data (post #s(hash-table data (title "foo")))) '(post title)) => "foo" ; map (ht-get* (ht ('post (ht ('title "foo")))) 'post 'title) => "foo" ; ht (h-get* (h* 'post (h* 'title "foo")) 'post 'title) => "foo" ; xht (h-let (h* 'post (h* 'title "foo")) .post.title) => "foo" ; xht
;;; nested alist (map-nested-elt '((post . ((title . "foo")))) '(post title)) => "foo" ; map (h-get* (h<-it '((post . ((title . "foo"))))) 'post 'title) => "foo" ; xht (let-alist '((post . ((title . "foo")))) .post.title) => "foo" ; let-alist (h-let-it '((post . ((title . "foo")))) .post.title) => "foo" ; xht
;;; nested plist (map-nested-elt '(post (title "foo")) '(post title)) => "foo" ; map (h-get* (h<-it '(post (title "foo"))) 'post 'title) => "foo" ; xht (h-let-it '(post (title "foo")) .post.title) => "foo" ; xht
Mapping Over Maps
map-apply
;;; hash table (map-apply (lambda (k v) (cons k v)) #s(hash-table data (x 1 y 2))) ; map (map-apply (-cut cons <> <>) #s(hash-table data (x 1 y 2))) ; map + dash (map-apply (##cons % %2) #s(hash-table data (x 1 y 2))) ; map + llama (h-lmap (lambda (k v) (cons k v)) (h* 'x 1 'y 2)) ; xht (nreverse (ht->alist (ht ('x 1) ('y 2)))) ; ht (h-lmap (-cut cons <> <>) (h* 'x 1 'y 2)) ; xht (h--lmap (cons key value) (h* 'x 1 'y 2)) ; xht (h-lmap (##cons % %2) (h* 'x 1 'y 2)) ; xht + llama (h->alist (h* 'x 1 'y 2)) ; xht => '((x . 1) (y . 2))
;;; hash table (map-apply (lambda (k v) (format "%s = %s" k v)) #s(hash-table data (x 1 y 2))) ; map (map-apply (-cut format "%s = %s" <> <>) #s(hash-table data (x 1 y 2))) ; map + dash (nreverse (ht-amap (format "%s = %s" key value) (ht ('x 1) ('y 2)))) ; ht (map-apply (##format "%s = %s" % %2) #s(hash-table data (x 1 y 2))) ; map + llama (h-lmap (-cut format "%s = %s" <> <>) (h* 'x 1 'y 2)) ; xht (h--lmap (format "%s = %s" key value) (h* 'x 1 'y 2)) ; xht (h-lmap (##format "%s = %s" % %2) (h* 'x 1 'y 2)) ; xht + llama => '("x = 1" "y = 2")
;;; vector (map-apply (lambda (i e) `(,i ,e)) [a b c]) ; map (-map-indexed (lambda (i e) `(,i ,e)) (append [a b c] ())) ; dash (--map-indexed `(,it-index ,it) (append [a b c] ())) ; dash (-map-indexed (-cut list <> <>) (append [a b c] ())) ; dash (-map-indexed (##list % %2) (append [a b c] ())) ; dash + llama (h-lmap (lambda (i e) `(,i ,e)) (h<-it [a b c])) ; xht (h-lmap (-cut list <> <>) (h<-it [a b c])) ; xht (h--lmap (list key value) (h<-it [a b c])) ; xht (h--lmap `(,key ,value) (h<-it [a b c])) ; xht (h-lmap (##list % %2) (h<-it [a b c])) ; xht + llama (map-apply (-cut list <> <>) [a b c]) ; map + dash (map-apply (##list % %2) [a b c]) ; map + llama (h-items (h<-it [a b c])) ; xht => '((0 a) (1 b) (2 c))
map-do
;;; alist ;;;; map (let (result) (map-do (lambda (k v) (push (* k v) result)) '((1 . 2) (3 . 4) (5 . 6))) (nreverse result)) => '(2 12 30) ;;;; dash (let (result) (-each '((1 . 2) (3 . 4) (5 . 6)) (-lambda ((k . v)) (push (* k v) result))) (nreverse result)) => '(2 12 30) ;;;; xht (let (result) (h--each (h<-it '((1 . 2) (3 . 4) (5 . 6))) (push (* key value) result)) (nreverse result)) => '(2 12 30) (-map (-compose #'-product #'-value-to-list) '((1 . 2) (3 . 4) (5 . 6))) => '(2 12 30) ; dash (-map (-cut -> <> -value-to-list -product) '((1 . 2) (3 . 4) (5 . 6))) => '(2 12 30) ; dash (--map (-> it -value-to-list -product) '((1 . 2) (3 . 4) (5 . 6))) => '(2 12 30) ; dash (--map (-product (-value-to-list it)) '((1 . 2) (3 . 4) (5 . 6))) => '(2 12 30) ; dash (-map (-lambda ((k . v)) (* k v)) '((1 . 2) (3 . 4) (5 . 6))) => '(2 12 30) ; dash (h--lmap (* key value) (h<-it '((1 . 2) (3 . 4) (5 . 6)))) => '(2 12 30) ; xht (h-lmap (-cut * <> <>) (h<-it '((1 . 2) (3 . 4) (5 . 6)))) => '(2 12 30) ; xht (--map (* (car it) (cdr it)) '((1 . 2) (3 . 4) (5 . 6))) => '(2 12 30) ; dash
;;; hash table ;;;; map (with-output-to-string (map-do (lambda (key value) (princ (format "%s = %s\n" (upcase key) value))) #s(hash-table data ("a" 1 "b" 2 "c" 3)))) => "A = 1\nB = 2\nC = 3\n" ;;;; xht (with-output-to-string (h--each (h* "a" 1 "b" 2 "c" 3) (princ (format "%s = %s\n" (upcase key) value)))) => "A = 1\nB = 2\nC = 3\n"
map-keys
;;; alist (map-keys '((a . 1) (b . ((c . 2))))) => '(a b) ; map (ht-keys (ht<-alist '((a . 1) (b . ((c . 2)))))) => '(a b) ; ht (h-keys (h<-it '((a . 1) (b . ((c . 2)))))) => '(a b) ; xht (mapcar #'car '((a . 1) (b . ((c . 2))))) => '(a b) ; C src (-map #'car '((a . 1) (b . ((c . 2))))) => '(a b) ; dash
;;; hash table (map-keys #s(hash-table data (a 1 b #s(hash-table data (c 2))))) => '(a b) ; map (nreverse (ht-keys (ht ('a 1) ('b (ht ('c 2)))))) => '(a b) ; ht (h-keys (h* 'a 1 'b (h* 'c 2))) => '(a b) ; xht
map-keys-apply
;;; alist (map-keys-apply #'identity '((a) (b) (c))) => '(a b c) ; map (ht-keys (ht<-alist '((a) (b) (c)))) => '(a b c) ; ht (h-keys (h<-alist '((a) (b) (c)))) => '(a b c) ; xht (--map (car it) '((a) (b) (c))) => '(a b c) ; dash (mapcar #'car '((a) (b) (c))) => '(a b c) ; C src (-map #'car '((a) (b) (c))) => '(a b c) ; dash
;;; alist (map-keys-apply #'symbol-name '((a . 1) (b . 2))) => '("a" "b") ; map (ht-amap (symbol-name key) (ht<-alist '((a . 1) (b . 2)))) => '("a" "b") ; ht (-map (-compose #'symbol-name #'car) '((a . 1) (b . 2))) => '("a" "b") ; dash (h--lmap (symbol-name key) (h<-it '((a . 1) (b . 2)))) => '("a" "b") ; xht (-map (-cut -> <> car symbol-name) '((a . 1) (b . 2))) => '("a" "b") ; dash (--map (symbol-name (car it)) '((a . 1) (b . 2))) => '("a" "b") ; dash
;;; hash table (map-keys-apply #'symbol-name #s(hash-table data (a 1 b 2))) => '("a" "b") ; map (nreverse (ht-amap (symbol-name key) (ht ('a 1) ('b 2)))) => '("a" "b") ; ht (h--lmap (symbol-name key) (h* 'a 1 'b 2)) => '("a" "b") ; xht
map-values
;;; alist (map-values '((a . 1) (b . 2))) => '(1 2) ; map (ht-values (ht<-alist '((a . 1) (b . 2)))) => '(1 2) ; ht (h-values (h<-it '((a . 1) (b . 2)))) => '(1 2) ; xht (--map (cdr it) '((a . 1) (b . 2))) => '(1 2) ; dash (mapcar #'cdr '((a . 1) (b . 2))) => '(1 2) ; C src (-map #'cdr '((a . 1) (b . 2))) => '(1 2) ; dash
;;; hash table (map-values #s(hash-table data (:a 1 :b 2))) => '(1 2) ; map (nreverse (ht-values (ht (:a 1) (:b 2)))) => '(1 2) ; ht (h-values (h* :a 1 :b 2)) => '(1 2) ; xht
map-values-apply
;;; alist (map-values-apply #'1+ '((a . 1) (b . 2))) => '(2 3) ; map (ht-amap (1+ value) (ht<-alist '((a . 1) (b . 2)))) => '(2 3) ; ht (h--lmap (1+ value) (h<-it '((a . 1) (b . 2)))) => '(2 3) ; xht (-map (-compose #'1+ #'cdr) '((a . 1) (b . 2))) => '(2 3) ; dash (-map (-cut -> <> cdr 1+) '((a . 1) (b . 2))) => '(2 3) ; dash (--map (1+ (cdr it)) '((a . 1) (b . 2))) => '(2 3) ; dash
;;; hash table (map-values-apply #'1+ #s(hash-table data (:a 1 :b 2))) => '(2 3) ; map (nreverse (ht-amap (1+ value) (ht (:a 1) (:b 2)))) => '(2 3) ; ht (h--lmap (1+ value) (h* :a 1 :b 2)) => '(2 3) ; xht
Excerpting Maps
map-delete
;;; hash table ;;;; map ;; `setq' is necessary if you want to actually change `htbl' (let ((htbl #s(hash-table data (:x 1 :y 2 :z 3)))) (setq htbl (map-delete htbl :x)) htbl) H=> (h* :y 2 :z 3) ;;;; ht (let ((htbl (ht (:x 1) (:y 2) (:z 3)))) (ht-remove! htbl :x) htbl) H=> (h* :y 2 :z 3) ;;;; xht (let ((htbl (h* :x 1 :y 2 :z 3))) (h-rem! htbl :x) htbl) H=> (h* :y 2 :z 3)
;;; hash table ;;;; map ;; `setq' is not used if you want the result without changing `htbl' (let ((htbl #s(hash-table data (:x 1 :y 2 :z 3)))) (map-delete htbl :x)) H=> (h* :y 2 :z 3) ;;;; ht (let* ((htb1 (ht (:x 1) (:y 2) (:z 3))) (htb2 (ht-copy htb1))) (ht-remove! htb2 :x) htb2) H=> (h* :y 2 :z 3) ;;;; xht (let ((htbl (h* :x 1 :y 2 :z 3))) (h-rem htbl :x)) H=> (h* :y 2 :z 3)
;;; alist ;;;; map ;; `setq' is necessary if you want to actually change `alist' (let ((alist '((x . 1) (y . 2) (z . 3)))) (setq alist (map-delete alist 'x)) alist) => '((y . 2) (z . 3)) ;;;; ht (let* ((alist '((x . 1) (y . 2) (z . 3))) (htble (ht<-alist alist))) (ht-remove! htble 'x) (setq alist (ht->alist htble)) alist) => '((y . 2) (z . 3)) ;;;; xht (let ((alist '((x . 1) (y . 2) (z . 3)))) (setq alist (-> alist h<-it (h-rem 'x) h->alist)) alist) => '((y . 2) (z . 3))
;;; alist ;;;; map ;; `setq' is not used if you want the result without changing `alist' (let ((alist '((x . 1) (y . 2) (z . 3)))) (map-delete alist 'x)) => '((y . 2) (z . 3)) ;;;; ht (let* ((alist '((x . 1) (y . 2) (z . 3))) (htble (ht<-alist alist))) (ht-remove! htble 'x) (ht->alist htble)) => '((y . 2) (z . 3)) ;;;; xht (let ((alist '((x . 1) (y . 2) (z . 3)))) (-> alist h<-it (h-rem 'x) h->alist)) => '((y . 2) (z . 3))
map-filter
Note that map-filter
always returns an alist.
;;; hash table → alist (map-filter (lambda (_k v) (> v 2)) #s(hash-table data (:a 1 :b 2 :c 3))) ; map (-non-nil (->> (ht (:a 1) (:b 2) (:c 3)) (ht-map (lambda (k v) (and (> v 2) `(,k . ,v)))))) ; ht (-non-nil (->> (ht (:a 1) (:b 2) (:c 3)) (ht-amap (and (> value 2) (cons key value))))) ; ht (-non-nil (->> (ht (:a 1) (:b 2) (:c 3)) (ht-map (##and (> %2 2) `(,%1 . ,%2))))) ; ht + llama (-non-nil (->> (ht (:a 1) (:b 2) (:c 3)) (ht-map (-andfn (##> %2 2) #'cons)))) ; ht + llama (->> (ht (:a 1) (:b 2) (:c 3)) (ht-select (lambda (_k v) (> v 2))) ht->alist) ; ht (->> (h* :a 1 :b 2 :c 3) (h-lkeep (lambda (k v) (and (> v 2) `(,k . ,v))))) ; xht (->> (h* :a 1 :b 2 :c 3) (h--lkeep (and (> value 2) (cons key value)))) ; xht (->> (ht (:a 1) (:b 2) (:c 3)) ht->alist (--filter (> (cdr it) 2))) ; ht (->> (h* :a 1 :b 2 :c 3) (h-sel (lambda (_k v) (> v 2))) h->alist) ; xht (->> (ht (:a 1) (:b 2) (:c 3)) (ht-select (##> %2 2)) ht->alist) ; ht + llama (->> (h* :a 1 :b 2 :c 3) (h-lkeep (-andfn (##> %2 2) #'cons))) ; xht + llama (map-filter (##> %2 2) #s(hash-table data (:a 1 :b 2 :c 3))) ; map + llama (->> (h* :a 1 :b 2 :c 3) h->alist (--filter (> (cdr it) 2))) ; xht (->> (h* :a 1 :b 2 :c 3) (h--sel (> value 2)) h->alist) ; xht (->> (h* :a 1 :b 2 :c 3) (h-sel (##> %2 2)) h->alist) ; xht + llama => '((:c . 3))
The previous note about readability also applies here, so the last pure xht
version seems preferable.
;;; vector → alist (map-filter (lambda (i _e) (cl-evenp i)) [a b c d]) ; map (->> [a b c d] h<-it (h-lkeep (lambda (k v) (and (cl-evenp k) `(,k . ,v))))) ; xht (->> [a b c d] h<-it (h--lkeep (and (cl-evenp key) (cons key value)))) ; xht (->> [a b c d] h<-it h->alist (-filter (-compose #'cl-evenp #'car))) ; xht (->> [a b c d] h<-it (h-sel (lambda (k _v) (cl-evenp k))) h->alist) ; xht (->> [a b c d] h<-it h->alist (-filter (-cut -> <> car cl-evenp))) ; xht (->> [a b c d] h<-it (h-lkeep (-andfn (##cl-evenp % _%2) #'cons))) ; xht + llama (->> [a b c d] h<-it (h-lkeep (##and (cl-evenp %) `(,% . ,%2)))) ; xht + llama (->> [a b c d] h<-it h->alist (--filter (cl-evenp (car it)))) ; xht (->> [a b c d] h<-it (h-sel (##cl-evenp % _%2)) h->alist) ; xht + llama (->> [a b c d] h<-it (h--sel (cl-evenp key)) h->alist) ; xht (map-filter (##cl-evenp % _%2) [a b c d]) ; map + llama => '((0 . a) (2 . c))
map-remove
Note that map-remove
always returns an alist.
;;; hash table → alist (map-remove (lambda (_k v) (>= v 2)) #s(hash-table data (:a 1 :b 2 :c 3))) ; map (-non-nil (->> (ht (:a 1) (:b 2) (:c 3)) (ht-map (lambda (k v) (unless (>= v 2) `(,k . ,v)))))) ; ht (-non-nil (->> (ht (:a 1) (:b 2) (:c 3)) (ht-amap (unless (>= value 2) (cons key value))))) ; ht (-non-nil (->> (ht (:a 1) (:b 2) (:c 3)) (ht-map (##unless (>= %2 2) `(,% . ,%2))))) ; ht + llama (->> (h* :a 1 :b 2 :c 3) (h-lkeep (lambda (k v) (unless (>= v 2) `(,k . ,v))))) ; xht (->> (ht (:a 1) (:b 2) (:c 3)) (ht-reject (lambda (_k v) (>= v 2))) ht->alist) ; ht (->> (h* :a 1 :b 2 :c 3) (h--lkeep (unless (>= value 2) (cons key value)))) ; xht (->> (ht (:a 1) (:b 2) (:c 3)) ht->alist (-remove (-cut -> <> cdr (>= 2)))) ; ht (->> (ht (:a 1) (:b 2) (:c 3)) ht->alist (-remove (##>= (cdr %) 2))) ; ht + llama (->> (ht (:a 1) (:b 2) (:c 3)) ht->alist (--remove (>= (cdr it) 2))) ; ht (->> (h* :a 1 :b 2 :c 3) (h-lkeep (##unless (>= %2 2) `(,% . ,%2)))) ; xht + llama (->> (h* :a 1 :b 2 :c 3) h->alist (-remove (-cut -> <> cdr (>= 2)))) ; xht (->> (h* :a 1 :b 2 :c 3) (h-rej (lambda (_k v) (>= v 2))) h->alist) ; xht (->> (ht (:a 1) (:b 2) (:c 3)) (ht-reject (##>= %2 2)) ht->alist) ; ht + llama (map-remove (##>= %2 2) #s(hash-table data (:a 1 :b 2 :c 3))) ; map + llama (->> (h* :a 1 :b 2 :c 3) h->alist (-remove (##>= (cdr %) 2))) ; xht + llama (->> (h* :a 1 :b 2 :c 3) h->alist (--remove (>= (cdr it) 2))) ; xht (->> (h* :a 1 :b 2 :c 3) (h--rej (>= value 2)) h->alist) ; xht (->> (h* :a 1 :b 2 :c 3) (h-rej (##>= %2 2)) h->alist) ; xht + llama => '((:a . 1))
The previous note about readability also applies here, so the last pure xht
version seems preferable.
;;; vector → alist (map-remove (lambda (i _e) (cl-evenp i)) [a b c d]) ; map (->> [a b c d] h<-it (h-lkeep (lambda (i e) (unless (cl-evenp i) `(,i . ,e))))) ; xht (->> [a b c d] h<-it (h--lkeep (unless (cl-evenp key) (cons key value)))) ; xht (->> [a b c d] h<-it (h-lkeep (##unless (cl-evenp %) `(,% . ,%2)))) ; xht + llama (->> [a b c d] h<-it (h-rej (lambda (i _e) (cl-evenp i))) h->alist) ; xht (->> [a b c d] h<-it h->alist (-reject (-cut -> <> car cl-evenp))) ; xht (->> [a b c d] h<-it h->alist (-reject (##cl-evenp (car %)))) ; xht + llama (->> [a b c d] h<-it h->alist (--reject (cl-evenp (car it)))) ; xht (->> [a b c d] h<-it (h-rej (##cl-evenp % _%2)) h->alist) ; xht + llama (->> [a b c d] h<-it (h--rej (cl-evenp key)) h->alist) ; xht (map-remove (##cl-evenp % _%2) [a b c d]) ; map + llama => '((1 . b) (3 . d))
map-merge
;;; inputs of different types, alist output (map-merge 'list #s(hash-table data (x 1 y 2)) '((z . 3))) ; map (funcall (-on (-compose #'h->alist #'h-mix) #'h<-it) (h* 'x 1 'y 2) '((z . 3))) ; xht (h->alist (apply #'h-mix (-map #'h<-it `(,(h* 'x 1 'y 2) ((z . 3)))))) ; xht (h->alist (funcall (-on #'h-mix #'h<-it) (h* 'x 1 'y 2) '((z . 3)))) ; xht => '((x . 1) (y . 2) (z . 3))
;;; inputs of different types, hash table output (map-merge 'hash-table #s(hash-table data (x 1 y 2)) '((z . 3))) ; map (apply #'h-mix (-map #'h<-it `(,(h* 'x 1 'y 2) ((z . 3))))) ; xht (funcall (-on #'h-mix #'h<-it) (h* 'x 1 'y 2) '((z . 3))) ; xht H=> (h* 'x 1 'y 2 'z 3)
;;; hash table inputs, alist output (map-merge 'list #s(hash-table data (x 1 y 2)) #s(hash-table data (z 3))) ; map (h->alist (h-mix (h* 'x 1 'y 2) (h* 'z 3))) ; xht => '((x . 1) (y . 2) (z . 3))
;;; hash table inputs, hash table output (map-merge 'hash-table #s(hash-table data (x 1 y 2)) #s(hash-table data (z 3))) ; map (h-mix (h* 'x 1 'y 2) (h* 'z 3)) ; xht H=> (h* 'x 1 'y 2 'z 3)
map-merge-with
;;;; map (map-merge-with 'hash-table (lambda (v1 v2) (list v1 v2)) #s(hash-table data (x 1 y 2)) '((x . 3) (y . 4) (z . 5))) H=> (h* 'x '(1 3) 'y '(2 4) 'z 5) (map-merge-with 'list (lambda (v1 v2) (list v1 v2)) #s(hash-table data (x 1 y 2)) '((x . 3) (y . 4) (z . 5))) => '((x 1 3) (y 2 4) (z . 5))
;;;; xht ;; Let's define a temporary function that does the above (defun h-_merge-with (&rest maps) (let ((res (h-new))) (dolist (map (-map #'h<-it (nreverse maps)) res) (h--each map (h-put-add! res key value))))) ;; And then: (h-_merge-with (h* 'x 1 'y 2) '((x . 3) (y . 4) (z . 5))) H=> (h* 'x '(1 3) 'y '(2 4) 'z 5) (h->alist (h-_merge-with (h* 'x 1 'y 2) '((x . 3) (y . 4) (z . 5)))) => '((x 1 3) (y 2 4) (z . 5))
map-insert
;;; hash table (map-insert #s(hash-table data (x 1 y 2)) 'z 3) H=> (h* 'x 1 'y 2 'z 3) ; map (let ((tbl (ht ('x 1) ('y 2)))) (ht-set! tbl 'z 3) tbl) H=> (h* 'x 1 'y 2 'z 3) ; ht (h-put (h* 'x 1 'y 2) 'z 3) H=> (h* 'x 1 'y 2 'z 3) ; xht ;; ^non-destructive
;;; alist (new pair) (map-insert '((x . 1) (y . 2)) 'z 3) => '((z . 3) (x . 1) (y . 2)) ; map (-> '((x . 1) (y . 2)) h<-it (h-put 'z 3) h->alist) => '((x . 1) (y . 2) (z . 3)) ; xht
;;; alist (update pair) (map-insert '((x . 1) (y . 2)) 'y 5) => '((y . 5) (x . 1)) ; map (-> '((x . 1) (y . 2)) h<-it (h-put 'y 5) h->alist) => '((x . 1) (y . 5)) ; xht
map-put!
;;; hash table (let ((a #s(hash-table data (x 1 y 2)))) (map-put! a 'z 3) a) H=> (h* 'x 1 'y 2 'z 3) ; map (let ((a (ht ('x 1) ('y 2)))) (ht-set! a 'z 3) a) H=> (h* 'x 1 'y 2 'z 3) ; ht (let ((a (h* 'x 1 'y 2))) (h-put! a 'z 3) a) H=> (h* 'x 1 'y 2 'z 3) ; xht
Binding
map-let
;;; hash table (map-let (('one x) ('thr z)) #s(hash-table data (one 1 two 2 thr 3)) (list x z)) => '(1 3) ; map (-let [(&hash 'one x 'thr z) #s(hash-table data (one 1 two 2 thr 3))] (list x z)) => '(1 3) ; dash (-let [(&hash 'one 'thr) #s(hash-table data (one 1 two 2 thr 3))] (list one thr)) => '(1 3) ; dash (map-let (one thr) #s(hash-table data (one 1 two 2 thr 3)) (list one thr)) => '(1 3) ; map (h-let (h* 'one 1 'two 2 'thr 3) (list .one .thr)) => '(1 3) ; xht
;;; alist (map-let (('one x) ('thr z)) '((one . 1) (two . 2) (thr . 3)) (list x z)) => '(1 3) ; map (-let [(&alist 'one x 'thr z) '((one . 1) (two . 2) (thr . 3))] (list x z)) => '(1 3) ; dash (-let [(&alist 'one 'thr) '((one . 1) (two . 2) (thr . 3))] (list one thr)) => '(1 3) ; dash (-let [(x _ z) '((one . 1) (two . 2) (thr . 3))] (list (cdr x) (cdr z))) => '(1 3) ; dash (map-let (one thr) '((one . 1) (two . 2) (thr . 3)) (list one thr)) => '(1 3) ; map (let-alist '((one . 1) (two . 2) (thr . 3)) (list .one .thr)) => '(1 3) ; let-alist (h-let-it '((one . 1) (two . 2) (thr . 3)) (list .one .thr)) => '(1 3) ; xht
;;; plist (map-let (('one x) ('thr z)) '(one 1 two 2 thr 3) (list x z)) => '(1 3) ; map (-let [(&plist 'one x 'thr z) '(one 1 two 2 thr 3)] (list x z)) => '(1 3) ; dash (-let [(&plist 'one 'thr) '(one 1 two 2 thr 3)] (list one thr)) => '(1 3) ; dash (map-let (one thr) '(one 1 two 2 thr 3) (list one thr)) => '(1 3) ; map (h-let-it '(one 1 two 2 thr 3) (list .one .thr)) => '(1 3) ; xht
Closing thoughts
All of these are fine libraries with pros and cons.
Enjoy the many available options we have.
Source of native functions
I wrote in the beginning:
;;;; all else (native) (let-alist a x) ; let-alist (foo else) ; C source code, subr, or subr-x
Here are the details for that:
;;;; C source code (primitives), subr, or subr-x => (list "subr-x" '(hash-table-empty-p) "subr" '(alist-get dolist lambda plistp push) "C src" '(/ = and apply aref car cdr cons copy-alist copy-hash-table copy-sequence elt expt funcall gethash hash-table-count hash-table-p length let let* list mapcar nreverse plist-get setq))
Either of the below will get you (roughly) the above:
;;;;; dash solution (let* ((funs '(/ = alist-get and apply aref car cdr cons copy-alist copy-hash-table copy-sequence dolist elt expt funcall gethash hash-table-count hash-table-empty-p hash-table-p lambda length let let* list mapcar nreverse plist-get plistp push setq)) (libs (--group-by (if-let ((file (symbol-file it))) (file-name-base file) "C src") ; C source code = primitive funs))) (-mapcat (-lambda ((a . b)) `(,a ,b)) (nreverse libs)))
;;;;; xht solution (let* ((funs '(/ = alist-get and apply aref car cdr cons copy-alist copy-hash-table copy-sequence dolist elt expt funcall gethash hash-table-count hash-table-empty-p hash-table-p lambda length let let* list mapcar nreverse plist-get plistp push setq)) (htbl (h-new (length funs)))) (dolist (fun (nreverse funs)) (h-put-add! htbl (if-let ((file (symbol-file fun))) (file-name-base file) "C src") ; C source code = primitive fun)) (h->plist htbl))
Acknowledgments
;; I wrote the non-map solutions, plus several map usage examples ;; SPDX-FileCopyrightText: © flandrew ;; ;; Many other map usage examples came from elisp-demos ;; SPDX-FileCopyrightText: © 2018-2020 Xu Chunyang ;; ;; All is under the same license ;; SPDX-License-Identifier: GPL-3.0-or-later
See also
If you liked this, you may want to see the same idea applied to seq.el.