Why does Enumerable#find/#detect return an Array even when called on an Hash?

arrays, enumerable, hash, ruby

Solution

The `Hash#detect` is inherited from `Enumerable#detect` method.

`Enumerable` module generates multiple methods(such as `sort`, `min`, `max` including `detect` etc.) based on the `each` method of the class which includes `Enumerable`.

It doesn't care about how `each` is implemented as long as it

"...yields successive members of the collection. " from ruby-doc

So for the `Hash#detect` method, it relies on `Hash#each`'s behavior, which is:

Calls block once for each key in hsh, passing the key-value pair as parameters. If no block is given, an enumerator is returned instead.

h = { "a" => 100, "b" => 200 }
h.each {|key, value| puts "#{key} is #{value}" }

Because `Hash#each` yields the hash as two pair array, all methods inherited from the `Enumerable` module works based on that.

That's why `Hash#detect` produces a two elements array instead of the an hash object itself.

Problem

The documentation for `Enumerable#find`/`#detect` says: `find(ifnone = nil) { |obj| block } → obj or nil` `find(ifnone = nil) → an_enumerator` Passes each entry in enum to block. Returns the first for which block is not false. If no object matches, calls ifnone and returns its result when it is specified, or returns `nil` otherwise. However, when it is called on the Hash, the result has changed the type to Array instead of the original Hash. Is it some implementation fault or some historical conventions regarding this datatype? ``` {a: 'a', b:'b'}.find {|k, v| v == 'b'} # => [:b, 'b'] ```

Original source