Project

General

Profile

Actions

Feature #5553

closed

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

Added by sawa (Tsuyoshi Sawada) over 12 years ago. Updated over 11 years ago.

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

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 12 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 12 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 12 years ago

  • Status changed from Open to Feedback

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

Updated by alexeymuranov (Alexey Muranov) over 12 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 11 years ago

  • Status changed from Feedback to Rejected

No feedback, looks hopeless to me. Closing.

--
Yusuke Endoh

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0