Feature #14788


`Hash#keys` Could Accept a Block

Added by rringler (Ryan Ringler) over 4 years ago. Updated over 2 years ago.

Target version:


Sometimes I only need to fetch some of the keys from a Hash. With the current Hash#keys implementation, this requires fetching all the keys and then selecting the ones I'm interested in. It would be nice if Has#keys accepted a block, and only returned the keys for which the block evaluated to true.


{ 1 => '1', 2 => '2', 3 => '3', 4 => '4' } { |key| key.odd? } # => [1, 3]


{ 1 => '1', 2 => '2', 3 => '3', 4 => '4' }.keys { |key| key.odd? } # => [1, 3]

The attached patch shows how rb_hash_keys might be modified to check for a passed block.


hash_keys_block.patch (2.27 KB) hash_keys_block.patch rringler (Ryan Ringler), 05/28/2018 06:13 AM
Actions #1

Updated by rringler (Ryan Ringler) over 4 years ago

  • Tracker changed from Bug to Feature
  • Backport deleted (2.3: UNKNOWN, 2.4: UNKNOWN, 2.5: UNKNOWN)
Actions #2

Updated by rringler (Ryan Ringler) over 4 years ago

Updated by Hanmac (Hans Mackowiak) over 4 years ago

I don't like it.

From keys {|k| k.something } I wouldn't know what it does with the keys, if they would select or map the keys or both.

Is probably against that "least surprise" thing

Updated by sawa (Tsuyoshi Sawada) over 4 years ago

If I were you, I would rather request a new method named Hash#select_keys for that purpose.

Updated by shevegen (Robert A. Heiler) over 4 years ago

I personally understand what Ryan suggested and meant; the example shows that he
can integrate the ".select" step .keys, through the use of block.

To me the example is also readable and the intent is clear, so in that particular
case, "object.odd?" queries whether the object in question, aka our key, is odd
or is not. Although one limitation obviously is that the object must respond to
.odd? or we'd otherwise safeguard calling on it (and return false in this case,
I guess?).

I think that the .select option is still clearer though, although more verbose.
In ruby I think it is really "rubyish" to use .select .reject, or the new alias
.filter. It's very clear and very simple IMO.

If the primary concern of the issue is about speed and efficiency, then I think
what Tsuyoshi Sawada makes sense, and a new method should be added (and you may
have to convince matz; I think Tsuyoshi suggested lots of features, some of
which were accepted).

If the primary aim is to omit .select, though, then I am not sure if the use
case described is ideal since it referred to speed/efficiency mostly (in other
words, to not get a full array result when we use .keys there).

Hanmac wrote:

Is probably against that "least surprise" thing

Well, there is not really "one universal least surprise". Matz said
that once, when people are different, have different expectations etc..
What to expect of C++ too. :-)

In this case, I somewhat agree with your point, although I am actually
neutral since I don't mind either way.

Anyway, I guess it is up to Ryan whether he wants to modify the suggestion
or not; it's his suggestion after all. I would recommend to also consider
Tsuyoshi's suggestion though, even though it is different (e. g. a new
method, versus modify an existing method). I guess ultimately it comes
down as to what any ruby hacker would naively assume .keys on class
Hash to do when a block is issued to it. I myself would not know, but
as said, I am mostly really neutral here, just adding some thoughts.

Updated by znz (Kazuhiro NISHIYAMA) over 4 years ago

How about each_key?

{ 1 => '1', 2 => '2', 3 => '3', 4 => '4' } # => [1, 3]

Updated by jacobevelyn (Jacob Evelyn) over 2 years ago

Just noting that I have a similar (but different) proposal in #16739.


Also available in: Atom PDF