Feature #7793
closedNew methods on Hash
Description
It would be nice to have the following methods added to hash
h = { name: 'dominic', request: 'add the following methods', :why => 'convenience'}
h.map_v{|v| v.upcase}
#=> {:name=>"DOMINIC", :request=>"ADD THE FOLLOWING METHODS", :why=>"CONVENIENCE"}
h.map_k{|k| k.to_s}
#=> { "name"=> 'dominic', "request"=>"add the following methods', "why" => "convenience"}
h.map_kv{|k,v| [k.to_s, v.upcase]}
#=> { "name"=>"DOMINIC", "request"=>"ADD THE FOLLOWING METHODS", "why"=>"CONVENIENCE"}
class Hash
def map_v
reduce({}) do |result, array|
k,v = array
new_val = yield v
result.merge( k => new_val)
end
end
def map_k
reduce({}) do |result, array|
k,v = array
new_k = yield k
result.merge(new_k => v)
end
end
def map_kv
reduce({}) do |result, array|
new_k,new_v = yield array
result.merge(new_k => new_v)
end
end
end
Updated by marcandre (Marc-Andre Lafortune) almost 12 years ago
- Status changed from Open to Closed
I am glad to see that more people like you take the time to propose ways to create hashes.
I completely agree that hash creation from Enumerable is lacking currently.
I will close this feature request because I am convinced it can't be accepted as is (the proposed names have no chance of being accepted) and because it is largely duplicated by the following:
https://bugs.ruby-lang.org/issues/6669
https://bugs.ruby-lang.org/issues/4151
https://bugs.ruby-lang.org/issues/7292
If you have the time, read on those and see if you can contribute.
Thanks
Updated by dsisnero (Dominic Sisneros) almost 12 years ago
This should be re-opened. It is not for all enumerables but only for hash.
map_v and map_k are very useful
map_kv is similar to h.mash and others and could be eliminated by those other bugs but the other functions aren't and are specifically for hashes and thus this should be re-opened
Updated by marcandre (Marc-Andre Lafortune) almost 12 years ago
- Category set to core
- Status changed from Closed to Assigned
- Assignee set to matz (Yukihiro Matsumoto)
Fine, I'll reopen and assign this to Matz.
Updated by Anonymous almost 12 years ago
At the risk of bike shedding, I think map_k and map_v should be named map_keys and map_values. That can be for matz to decide though.
Updated by nobu (Nobuyoshi Nakada) almost 12 years ago
Considering existing methods:
$ ruby -e 'p Hash.instance_methods(false).grep(/each_/)'
[:each_value, :each_key, :each_pair]
They should be map_key
, may_value
, and map_pair
, respectively, I
think.
Anyway, why don't you make it a gem first?
Updated by nobu (Nobuyoshi Nakada) almost 12 years ago
- Description updated (diff)
Updated by yhara (Yutaka HARA) almost 12 years ago
- Target version set to 2.6
Updated by phluid61 (Matthew Kerwin) almost 12 years ago
nobu (Nobuyoshi Nakada) wrote:
Anyway, why don't you make it a gem first?
That's a good idea. Let's see what the uptake is, if any: https://rubygems.org/gems/hashmap
Note: I used #map_keys, #map_values and #map_pairs as my method names.
Updated by Ajedi32 (Ajedi32 W) over 10 years ago
FYI, Rails has a method similar to the proposed map_k
called transform_keys
.
Updated by nobu (Nobuyoshi Nakada) over 10 years ago
- Has duplicate Feature #9970: Add `Hash#map_keys` and `Hash#map_values` added
Updated by nobu (Nobuyoshi Nakada) over 10 years ago
- Description updated (diff)
Updated by trans (Thomas Sawyer) over 10 years ago
An issue with the name is that "map" semantically means to create an Array, i.e.ahash.map{ |k,v| ... }
produces an Array. So map_keys
would make sense to mean ahash.map_keys{ |k| ... }
and produce an Array too. Hash#map_pair
would just a synonym for #map
, just as #each_pair
is just a synonym for #each
.
Facets has long had Hash#rekey
and Hash#revalue
(and in-place forms Hash#rekey!
and Hash#revalue!
). These names are concise and do not suffer this semantic issue. Note Facets doesn't have a #remap
method (though I suppose it could) because it has Enumerable#mash, and it's alias #graph, which can create a Hash from any Enumerable object.
Updated by sawa (Tsuyoshi Sawada) about 10 years ago
Just like there are map
and map!
, there should be both a non-desctructive and a destructive version for this method.
h = {a: "foo"}
h.non_destructive_one{|k, v| [k.to_s, v.upcase]} #=> {"a" => "FOO"}
h #=> {a: "foo"}
h.destructive_one!{|k, v| [k.to_s, v.upcase]} #=> {"a" => "FOO"}
h #=> {"a" => "FOO"}
I also have a (not that strong) opinion that the block for these methods should take a hash rather than an array. That should make more sense since the return value is a hash.
h.non_destructive_one{|k, v| {k.to_s => v.upcase}} #=> {"a" => "FOO"}
h.destructive_one!{|k, v| {k.to_s => v.upcase}} #=> {"a" => "FOO"}
Updated by avit (Andrew Vit) about 10 years ago
the block for these methods should take a hash rather than an array.
Do you mean the input should be a single argument with a hash? I don't think that is very consistent for |k, v|
expansion.
That should make more sense since the return value is a hash.
Everything inside the block is a tuple; what type the input/output are transformed from/to happens outside the block. IMHO the array makes more sense than the hash inside the block.
Updated by Ajedi32 (Ajedi32 W) about 10 years ago
Below is a summary of the different naming proposals so far in this thread, with links to the documentation for real-world implementations where available.
Option 1¶
The original proposal, uses the term map
to express changing the keys or values on the hash, and keeps things terse by abbreviating the terms 'key' and 'value':
Hash#map_k
Hash#map_k!
Hash#map_vs
Hash#map_v!
Hash#map_kv
Hash#map_kv!
Option 2¶
A clearer, more verbose alternative to option 1. (Proposed by Charlie Somerville.)
Option 3¶
Given the existing methods Hash#each_key
, Hash#each_value
, and Hash#each_pair
, it might be better to use a sigular alternative to option 2. (Proposed by Nobuyoshi Nakada.)
Hash#map_key
Hash#map_key!
Hash#map_value
Hash#map_value!
Hash#map_pair
Hash#map_pair!
Option 4¶
Given the potential for the previous options to be confused with Hash#map
, which returns an array, it might be best to use an entirely different naming convention. This one is based on Facets, a popular (485,329 downloads on Rubygems) library with the purpose of extending Ruby's core classes with useful methods. (Proposed by Thomas Sawyer.)
Hash#rekey
Hash#rekey!
Hash#revalue
Hash#revalue!
-
Enumerable#graph
(See #6669)- Aliased as
Enumerable#mash
- Aliased as
-
Hash#graph!
- Aliased as
Hash#mash!
- Aliased as
Option 5¶
Similar to option 4, but based on the naming convention used by Ruby on Rails.
Hash#transform_keys
Hash#transform_keys!
Hash#transform_values
Hash#transform_values!
Hash#transform_pairs
Hash#transform_pairs!
Updated by trans (Thomas Sawyer) about 10 years ago
I can't help but mention it, because it gave me a chuckle....
I like rekey
and revalue
from #4, because they make sense semantically, don't confuse the idea of map
returning an array, and they are concise. Concision is always a big plus. However graph
and mash
don't really convey much in their names (mash
is combination of "map" and "hash" btw), so I've always been rather ho-hum about those, but never could come up with a better, yet still concise, alternative.
Options #2 and #5 are nice for their consistency --the use of _keys
, _values
and _pairs
-- But they lack for concision (especially #5) which sucks, and #2 has the map name issue as mentioned.
So I tried a combination of both ideas using re-
as the prefix to the three suffixes and got:
rekey
revalue
repair
At which point the giggles kicked in :-)
Updated by Ajedi32 (Ajedi32 W) about 10 years ago
"repair"? Hehe, yeah that's kind of an unfortunate coincidence.
The thing I really like about Option 4's graph
and mash
is that they are methods on Enumerable
, which means they can be used with any Enumerable object, not just hashes. As I mentioned, the creation of a method like that is being discussed in #6669. Right now, a similar effect can be achieved (for the non-destructive method anyway) by chaining map
and to_h
, so perhaps the full hash transform methods don't provide as big of a benefit over what we have now as rekey
and revalue
do.
If we do decide to base our names off of the assumption that the full hash transform methods will be on Enumerable
, and not Hash
, then perhaps something like this might work:
Enumerable#associate
Hash#associate!
Updated by duerst (Martin Dürst) about 10 years ago
- Related to Feature #10552: [PATCH] Add Enumerable#frequencies and Enumerable#relative_frequencies added
Updated by rafaelfranca (Rafael França) over 9 years ago
I'm biased here since we already implemented part of Option #5 on Ruby on Rails but I prefer its explicitness over concision. It is not clear to me what rekey
and revalue
does.
Updated by mrkn (Kenta Murata) over 8 years ago
- Related to Feature #12512: Import Hash#transform_values and its destructive version from ActiveSupport added
Updated by jeremyevans0 (Jeremy Evans) about 3 years ago
- Status changed from Assigned to Closed
This feature was implemented as transform_keys
, transform_values
, and to_h
.