Feature #13583
closedAdding `Hash#transform_keys` method
Description
In 2.4, new useful method Hash#transform_values
was added. I would like to propose also adding matching method Hash#transform_keys
.
{a: 1, b: 2}.transform_keys { |k| k.to_s }
=> {"a"=>1, "b"=>2}
What needs to be considered is what to do in case of two keys mapping to the same new key
{ a: 1, b: 2 }.transform_keys {|_| :same_key } # what should happen?
I think using Hash[]
as model behaviour is a good idea.
Hash[{ a: 1, b: 2 }.map { |key, value| [:s, value] }]
=> {:s=>2}
it's also how Hash#transform_keys
works in rails (afaict).
This is a follow up feature request to #9970, which seems to be stalled. If the behaviour can be agreed upon, I can try putting together a patch (if no one else wants to step up).
Updated by shyouhei (Shyouhei Urabe) over 7 years ago
Thank you for issuing this. I see there is an obvious needs for this transformation (stringify_keys) so I'm :+1: to the feature.
Let's have a concrete definition of this requested method:
- Its name is Hash#transform_keys.
- It returns a newly created Hash instance.
- It has zero arity.
- It yields,
- with only one block parameter (which is a key of the original hash),
- the evaluated value is the new key for the entry.
- When the new key conflicts, later entry silently discards former entry (see the description of this issue).
Is it okey? Am I missing something? Do people have any opinion?
Updated by graywolf (Gray Wolf) over 7 years ago
I don't think you missed anything, except I would just point out to also add Hash#transform_keys!
. I don't know if it's worth mentioning or just kinda automatically assumed.
Updated by shevegen (Robert A. Heiler) over 7 years ago
I think the names are good, both #transform_keys and #transform_values.
Seem quite clear to me from the names.
On the linked older issue (-3 years), the names were different,
Hash#map_keys and Hash#map_values. Matz said that the names may
be confusing. Perhaps #transform_keys and #transform_values
are better names. (I have not checked if the proposal is the
very same; shyouhei provided a very specific definition here,
including behaviour such as arity and yield-situations, which
I think the other proposal did not have). Guess matz will have
a look.
graywolf, could you perhaps show some example documentation for
the two methods?
Updated by matz (Yukihiro Matsumoto) over 7 years ago
Looks good to me.
Matz.
Updated by mrkn (Kenta Murata) over 7 years ago
- Status changed from Open to Closed
Applied in changeset trunk|r59328.
hash.c: Add Hash#transform_keys and Hash#transform_keys!
-
hash.c (transform_keys_i, rb_hash_transform_keys): Add Hash#transform_keys.
[Feature #13583] [ruby-core:81290] -
hash.c (rb_hash_transform_keys_bang): Add Hash#transform_keys!.
[Feature #13583] [ruby-core:81290] -
test/ruby/test_hash.rb: Add tests for above changes.
Updated by marcandre (Marc-Andre Lafortune) about 7 years ago
- Status changed from Closed to Open
- Assignee set to matz (Yukihiro Matsumoto)
I'm not sure I like the current behavior of transform_keys!
.
Two possibilities: transform_keys!
is each_key { delete(old_key), set(new_key) }
(as is currently) or replace(transform_keys)
(I think I prefer this).
Matz, could you confirm what behavior you want?
Current:
h = {1 => :hello, 2 => 'world'}
h.transform_keys(&:succ) # => {2 => :hello, 3 => 'world'}
h.transform_keys!(&:succ) # => {3 => :hello}
With using replace
, we'd get the same results.
The current behavior allows partial updates though:
h = {1 => :hello, 2 => :world}
h.transform_keys! { |k| k == 1 ? :one : break }
h # => {2 => world, :one => :hello}
With the replace
version, h
would be unchanged (or else we'd have to write an ensure
to do the partial update)
Updated by marcandre (Marc-Andre Lafortune) about 7 years ago
- Status changed from Open to Closed
Nevermind, I just remembered that ActiveSupport also defines transform_keys!
, so best match its behavior.