Feature #5553
closedA method for Hash that works differently depending on whether a key exists
Description
A method Hash#if_key(key, [default], &pr) which works like the following will be often used, and is useful.
a = {morning: "おはよう", daytime: "こんにちは", evening: "こんばんは", nothing: nil}
a.if_key(:morning){|str| "#{str}世界!"} #=> "おはよう世界!"
a.if_key(:nothing){|str| "#{str}世界!"} #=> "世界!"
a.if_key(:midnight){|str| "#{str}世界!"} #=> nil
a.if_key(:nothing, "どうも"){|str| "#{str}世界!"} #=> "どうも"
That is, when key' exists, then the corresponding value will be passed to
pr'. Otherwise, the given `default' or the implicit default will be returned.
Updated by alexeymuranov (Alexey Muranov) over 13 years ago
In your example, the :nothing key exists, so shouldn't it be
a.if_key(:nothing, "どうも"){|str| "#{str}世界!"} #=> "世界!" ?
Why would the code with this method be better than the following one:
if a.has_key?(key)
block here¶
else
default value or another block here¶
end
or ( a.has_key?(key) ? simple operation : default )
This seems easier to read.
If you plan to always use it with the same block, maybe it should be made into a separate class with an appropriate method?
Updated by sawa (Tsuyoshi Sawada) over 13 years ago
The whole point of this suggestion is that I feel some redundancy to have to call the method fetch
or []
on the hash after checking that the key exists using key?
. With this proposed method, the method call fetch
or []
is unnecessary, and you can just refer to a block variable.
Updated by matz (Yukihiro Matsumoto) over 13 years ago
- Status changed from Open to Feedback
「fetchや[]を頻繁に呼びたくない」という意図は伝わらないでも無いですが、でもそれよりもif_keyメソッドのほうが嬉しい局面というのがあまり想像できません。単にメソッドでラップすればいいんじゃないですか?
正直、この例では全然嬉しくないと思います。もうちょっと人工的でない例だと嬉しさがイメージできるかもしれません。
Updated by alexeymuranov (Alexey Muranov) over 13 years ago
I see. But then you may also want to pass a second procedure which is run if the key does not exist, and to have both the key and the value passed to the procedures, that is to introduce a method of the form #fetch_and_use(key, proc_if_key_found, proc_if_not). I am afraid that optimizing like this for all possible use cases may require introducing many new methods.
On the other hand, if you want to use a hash in this way with same procedures multiple times, then what about adding a method Hash#on_found_proc=
analogous to Hash#default_proc=
to be able to do like this:
a = {morning: "おはよう", daytime: "こんにちは", evening: "こんばんは", nothing: nil}
a.default = "どうも"
a.on_found_proc = proc do |hash,key,value|
"#{value}世界!"
end
a[:morning] # => "おはよう世界!"
a[:night] # => "どうも"
Update 4/11/2011. If the goal is to simply avoid searching for the key twice, then there is Hash#assoc
:
key_if_exists, value = h.assoc(key)
if key_if_exists.nil?
puts "Bad key"
else
puts "The value is #{value}"
end
However, if you use nil
as a key, then this will not work. It is still possible to do
if pair = h.assoc(key)
puts "The value is #{pair[1]}"
else
puts "Bad key"
end
Or to introduce Hash#paranoiac_fetch
:) :
key_exists, value = h.paranoiac_fetch(key)
if key_exists
puts "The value is #{value}"
else
puts "Bad key"
end
As to the original proposal, maybe defining some kind of a lazy Hash#map_values(&block)
would be a better? (Or Hash#on_found_proc=(&block)
, that would behave identically.)
I saw #4890, but there it is exclusively about Enumerable.
Updated by mame (Yusuke Endoh) over 12 years ago
- Status changed from Feedback to Rejected
No feedback, looks hopeless to me. Closing.
--
Yusuke Endoh mame@tsg.ne.jp