Last edit
Summary: group-by and hash-to-alist
Added:
> == Grouping ==
> Here's a function that uses a *hash table* to group things.
> (defun group-by (l func)
> (let ((out (make-hash-table :test 'equal)))
> (dolist (x l)
> (push x (gethash (funcall func x) out)))
> out))
> Here's a function to create an *alist* from a hash table.
> (defun hash-to-alist (hash)
> (let (alist)
> (maphash (lambda (key value)
> (push (cons key value) alist)) hash)
> alist))
> Example:
> (hash-to-alist (group-by '(1 2 3 4 5 6 7 8 9)
> (lambda (n) (/ n 3))))
> => ((3 9) (2 8 7 6) (1 5 4 3) (0 2 1))
How about moving these to the newer ElispCookbook? – AaronHawley
This function works only on lists of strings. Some functions such as directory-files already allow you to filter the result according to a regular expression, which is why you don’t often need this function.
(defun regexp-filter (regexp list)
"Filter LIST of strings with REGEXP."
(let (new)
(dolist (string list)
(when (string-match regexp string)
(setq new (cons string new))))
(nreverse new)))Examples:
(regexp-filter "ne" '("one" "two" "three" "four" "five"
"six" "seven" "eight" "nine" "ten"))
=> ("one" "nine")
(regexp-filter "en" '("one" "two" "three" "four" "five"
"six" "seven" "eight" "nine" "ten"))
=> ("seven" "ten")You can generalize the filter above and rewrite the regexp-filter using the general filter.
(defun filter (predicate list)
"Filter LIST using PREDICATE.
PREDICATE is called for every item in LIST. All items for which
PREDICATE returns non-nil are returned in a list."
(let (new)
(dolist (item list)
(when (funcall predicate item)
(setq new (cons item new))))
(nreverse new)))
(defun regexp-filter (regexp list)
"Filter LIST of strings by including all items matching REGEXP."
(filter (lambda (str) (string-match regexp str)) list))
(defun regexp-not-filter (regexp list)
"Filter LIST of strings by including all items matching REGEXP."
(filter (lambda (str) (not (string-match regexp str))) list))Examples:
(regexp-filter "ne" '("one" "two" "three" "four" "five"
"six" "seven" "eight" "nine" "ten"))
=> ("one" "nine")
(regexp-not-filter "en" '("one" "two" "three" "four" "five"
"six" "seven" "eight" "nine" "ten"))
=> ("one" "two" "three" "four" "five" "six" "eight" "nine")The filter function above, like any operation taking a predicate as parameter, should be written as a macro because Emacs uses DynamicScoping.
(defmacro map-delq-nil (predicate list)
"Filter LIST using PREDICATE.
PREDICATE is called for every item in LIST. All items for which
PREDICATE returns non-nil are returned in a list."
`(delq nil (mapcar ,predicate ,list)))(def-edebug-spec map-delq-nil (function-form form))
(defun exchange-list-elements (a b list)
"Exchange elements number A and B in LIST.
The elements are numbered starting with 0.
Return a copy of the list with the elements exchanged."
(let* ((l1 (copy-sequence list))
(l2 (cdr (nthcdr a l1)))
(l3 (cdr (nthcdr b l1))))
(setcdr (nthcdr (1- b) l1) nil); limit length of l2
(setcdr (nthcdr (1- a) l1) nil); limit length of l1
(nconc l1 (list (nth b list))
l2 (list (nth a list))
l3)))Example:
(exchange-list-elements 3 5 '(a b c d e f g h))
=> (a b c f e d g h)Note, ‘exchange-list-elements’ returns a copy of the LIST arg and does not modify it.
(setq *not-mutated-global* '(a b c d e f g h))
=> (a b c d e f g h) (let ((not-mutated *not-mutated-global*)
cmpr)
(setq cmpr
`((:exchanged ,(tt--exchange-list-elements 3 5 not-mutated))
(:original ,not-mutated)))
(setq cmpr `(:equal-exchange-oririnal ,(equal (cadr (assq :exchanged cmpr))
(cadr (assq :original cmpr)))
,@cmpr))) => (:equal-exchange-oririnal nil
(:exchanged (a b c f e d g h)) ; elt 'd' is now 'e'
(:original (a b c d e f g h))) ; the original list remains unchanged => *not-mutated-global*
=> (a b c d e f g h) ; the global value is unchangedFollowing are destructive operations. These do mutate their LIST argument.
(defun list-insert-after-elt (list aft-el el)
"Destructive insert EL after AFT-EL in LIST."
(push el (cdr (member aft-el list)))
list)
(defun list-insert-before-elt (list bef-el el)
"Destructive insert EL before BEF-EL in LIST."
(nreverse (insert-after (nreverse list) bef-el el)))
(defun list-set-elt (list old-el new-el)
"Destructive set OLD-EL to NEW-EL in LIST."
(setcar (member old-el list) new-el)
list)
(defun list-exchange-elt (list el1 el2)
"Desturctive exchange places of EL1 and EL2 in LIST."
(when (or (null (member el1 list))
(null (member el2 list)))
(error "el1 or el2 is not in list"))
(list-set-elt list el1 el2)
(list-set-elt list el2 el1))
(list-set-elt list el1 'this-fake-name1-should-not-be-in-list)
(list-set-elt list el2 el1)
(list-set-elt list 'this-fake-name1-should-not-be-in-list el2))Example:
(list-insert-after-elt '(1 2 4) 2 5)
=> (1 2 5 4)
(list-insert-before-elt '(1 2 3) 1 0)
=> (0 1 2 3)
(list-list-set-elt '(1 3 3) 3 2)
=> (1 2 3)
(list-exchange-elt '(1 3 2) 2 3)
=> (1 2 3)Here’s a function that uses a hash table to group things.
(defun group-by (l func)
(let ((out (make-hash-table :test 'equal)))
(dolist (x l)
(push x (gethash (funcall func x) out)))
out))Here’s a function to create an alist from a hash table.
(defun hash-to-alist (hash)
(let (alist)
(maphash (lambda (key value)
(push (cons key value) alist)) hash)
alist))Example:
(hash-to-alist (group-by '(1 2 3 4 5 6 7 8 9)
(lambda (n) (/ n 3))))
=> ((3 9) (2 8 7 6) (1 5 4 3) (0 2 1))