Feature #5553

A method for Hash that works differently depending on whether a key exists

Added by Tsuyoshi Sawada over 2 years ago. Updated over 1 year ago.

[ruby-dev:44779]
Status:Rejected
Priority:Normal
Assignee:-
Category:-
Target version:-

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 topr'. Otherwise, the given `default' or the implicit default will be returned.

History

#1 Updated by Alexey Muranov over 2 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?

#2 Updated by Tsuyoshi Sawada over 2 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.

#3 Updated by Yukihiro Matsumoto over 2 years ago

  • Status changed from Open to Feedback

「fetchや[]を頻繁に呼びたくない」という意図は伝わらないでも無いですが、でもそれよりもif_keyメソッドのほうが嬉しい局面というのがあまり想像できません。単にメソッドでラップすればいいんじゃないですか?
正直、この例では全然嬉しくないと思います。もうちょっと人工的でない例だと嬉しさがイメージできるかもしれません。

#4 Updated by Alexey Muranov over 2 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 #fetchanduse(key, procifkeyfound, procif_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.onfoundproc = 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:

keyifexists, value = h.assoc(key)
if keyifexists.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 :) :

keyexists, value = h.paranoiacfetch(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.

#5 Updated by Yusuke Endoh over 1 year ago

  • Status changed from Feedback to Rejected

No feedback, looks hopeless to me. Closing.

Yusuke Endoh mame@tsg.ne.jp

Also available in: Atom PDF