Feature #5553

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

Added by sawa (Tsuyoshi Sawada) over 5 years ago. Updated over 4 years ago.

Target version:


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.


#1 [ruby-dev:44781] Updated by alexeymuranov (Alexey Muranov) over 5 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
# default value or another block here

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 [ruby-dev:44786] Updated by sawa (Tsuyoshi Sawada) over 5 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 [ruby-dev:44792] Updated by matz (Yukihiro Matsumoto) over 5 years ago

  • Status changed from Open to Feedback


#4 [ruby-dev:44795] Updated by alexeymuranov (Alexey Muranov) over 5 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|
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"
puts "The value is #{value}"

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]}"
puts "Bad key"

Or to introduce Hash#paranoiac_fetch :) :

key_exists, value = h.paranoiac_fetch(key)
if key_exists
puts "The value is #{value}"
puts "Bad key"

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 [ruby-dev:46554] Updated by mame (Yusuke Endoh) over 4 years ago

  • Status changed from Feedback to Rejected

No feedback, looks hopeless to me. Closing.

Yusuke Endoh

Also available in: Atom PDF