3 ms·
It's trivial to extend the SERIES library to allow collecting sums in a hash-table (It was just a cut-paste of the collect-hash function with setf changed to in
by aidenn0 3y ago
It's trivial to extend the SERIES library to allow collecting sums in a hash-table (It was just a cut-paste of the collect-hash function with setf changed to incf; further extending to allow generic modifications of values is an exercise left to the reader). That allows a shorter (but not as short as I'd like) implementation. It could be shorter if the output list were to have elements like ("one" 1) as the multiple-value-bind exists to reverse that ordering for unpacking the hash-table at the end.
Here's the result (if you wanted to further optimize and know that the result will fit in a fixnum (i.e. +/- 2^62 on modern implementations, you can change the "'number" to "'fixnum"):
(defun unique-sum (list)
(multiple-value-bind (keys values)
(scan-hash
(collect-hash-sum
(map-fn 'string #'cadr (scan list))
(map-fn 'number #'car (scan list))
:test #'equal))
(map-fn 'list #'list values keys)))
And the extension to SERIES:
(series::defS collect-hash-sum (keys values &rest option-plist)
"(collect-hash keys values :test :size :rehash-size :rehash-threshold)
Combines a series of keys and a series of values together into a hash
table. Duplicate keys sum the values. The keyword arguments specify
the attributes of the hash table to be produced. They are used as
arguments to MAKE-HASH-TABLE"
(fragl ((keys t) (values t) (option-plist))
((table))
((table t (apply #'make-hash-table option-plist)))
()
()
((incf (gethash keys table 0) values)) () () nil)
:optimizer
(series::apply-literal-frag
(list '(((keys t) (values t) (table)) ((table)) () ()
() ((incf (gethash keys table 0) values)) () () nil)
keys values `(make-hash-table ,@ option-plist)))
:trigger t)