Reputation: 3020
I want to map a function over the values in a hash table, like so:
(hash-map add1 (hash "apple" 1 "pear" 2))
=> #hash(("apple" . 2) ("pear" . 3))
Is there a library function to do this? It'd be good to have one that worked on immutable hashetables too.
I looked on PlaneT, but didn't see anything there.
Now, if this really doesn't exist, I'll go ahead and write it. What would the etiquette for getting this into racket? I just fork it on github and add it to the standard library (and the docs!) and submit a pull request? Or should I make it a planeT first, and then ask for it to be moved in? I'd like to help, but I just don't know what's the 'proper' way to go about it.
Upvotes: 8
Views: 11424
Reputation: 52344
Many years after this question was asked, Racket 8.6 added hash-map/copy
to do this. It passes both keys and values of an existing hash table to a map function that returns two values - a new key and value to use in a newly returned table.
> (hash-map/copy (hash "apple" 1 "pear" 2) (lambda (k v) (values k (+ v 1))))
'#hash(("apple" . 2) ("pear" . 3))
Upvotes: 2
Reputation: 267
Since hash-map creates a list that is not needed here, I would rather use hash-for-each.
(define (hash-update-all! h func)
(hash-for-each
h
(lambda (k v) (hash-set! h k (func v)))))
(define table (make-hash '((a . 1) (b . 2) (c . 3))))
(hash-update-all! table (curry * 100))
table
==> '#hash((c . 300) (a . 100) (b . 200))
Edit: I forgot that for can handle a hash-table:
(define (hash-update-all! h func)
(for ([(k v) h]) (hash-set! h k (func v))))
(hash-update-all! table (curryr / 10))
table
==> '#hash((c . 30) (a . 10) (b . 20))
Upvotes: 2
Reputation: 236004
In Racket you can use the hash-map
higher-order procedure, which normally returns a list with the values resulting from the application of the procedure received as parameter, but it can be adapted for modifying the map in-place. For example:
(define h (make-hash))
(hash-set! h "apple" 1)
(hash-set! h "pear" 2)
h
=> '#hash(("pear" . 2) ("apple" . 1))
The trick is passing a lambda
with the appropriate functionality:
(hash-map h (lambda (key value)
(let ((newval (add1 value)))
(hash-set! h key newval)
newval)))
h
=> '#hash(("pear" . 3) ("apple" . 2))
For a more general solution, try this implementation:
(define (mutable-hash-map! hash proc)
(hash-map hash (lambda (key value)
(hash-set! hash key (proc value))))
(void))
Upvotes: 2
Reputation: 12013
You can iterate through the values of a hash table using in-hash-values, and use regular for loops to map across them. The function in-hash-values
takes a hash and returns a sequence of the values, which can then be traversed with a for
loop.
Example:
(for/list ([elt (in-hash-values (hash "apple" 1 "pear" 2))])
(add1 elt))
Similarly, you can use sequence-map, though what you get back is another sequence rather than a list:
(sequence-map add1 (in-hash-values (hash "apple" 1 "pear" 2)))
Upvotes: 2
Reputation: 2540
There is a hash-map, but it returns a list [1]
You would have to write your own hash-map
to do exactly what you want.
#lang racket
(define (new-hash-map f h)
(make-immutable-hash (hash-map h (λ (k v) (cons k (f v))))))
(new-hash-map add1 (hash "apple" 1 "pear" 2))
; => '#hash(("pear" . 3) ("apple" . 2))
Another form you might be interested in is for/hash [2]:
#lang racket
(define (new-hash-map2 f h)
(for/hash ([(k v) (in-hash h)]) (values k (f v))))
(new-hash-map2 add1 (hash "apple" 1 "pear" 2))
; => '#hash(("pear" . 3) ("apple" . 2))
If you feel that this functionality should be included with Racket, patches are most welcome! The best way to contribute is to fork on github and submit a pull request.
Upvotes: 8