> This allows the method to work with arbitrary instances of map-like classes without making any assumptions about what class it was passed or how instances of that class are constructed. Another option is to do `hash.class.new` and assume the class has a zero argument constructor.

I think I'd prefer `hash.class.new`, especially since you were saying that `#dup` can be pretty expensive for large objects.

> `map_keys`, like `map_hash` allows keys to be removed and replaced with new keys, this requires a delete on the key and a re-insertion to maintain the hash-order.
>
> Since `map_vals` preserves the key, we can be more optimal and avoid the key removal and re-insertion.

If you start with an empty hash, this won't be an issue, right?