3

I noticed following difference in behavior when I use map, select, and other Enumerable methods.

Let's assume we have a hash like below:

h = {a: 1}

Below code prints the output of select as expected.

p h.select { |k, v| true }
#=> {:a=>1}

However, below code shows that output is an Enumerator, even when a block has been provided.

p h.select do |k, v| 
  true 
end
#=> #<Enumerator: {:a=>1}:select>

Any idea why this difference of behavior? I run into this issue often as I keep using inspect p while working, and this behavior derails my thought process quite often.

Wand Maker
  • 18,476
  • 8
  • 53
  • 87

3 Answers3

4

Operator precedence matters.

p h.select do |k, v| true end

is in fact executed as:

(p h.select) do |k, v| true end

while

p h.select { |k, v| true }

is treated as:

p (h.select { |k, v| true })

Enumerable#select, called without a block, returns an enumerator.

Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
2

I think @mudasobwa's answer is essentially correct, but I wanted to clarify what's actually happening with regard to precedence.

When you do this:

p h.select do |k, v| 
  true 
end

...you're actually doing this:

p(h.select) do |k, v| 
  true 
end

In other words, you're passing the result of h.select (with no arguments, which returns an Enumerator) as an argument to p and you're also passing a block argument to p. p apparently silently ignores the block argument.

Conversely, when you do this:

p h.select {|k, v| true }

...you're actually doing this:

p(h.select {|k, v| true })

That is, you're passing the result of h.select { |k, v| true } to p, which gives you the result you expect.

If you want to use the do...end syntax and get the result you want, you have to wrap the whole thing in parentheses:

p(h.select do |k, v| 
  true 
end)

It's not pretty, but it works.

Jordan Running
  • 102,619
  • 17
  • 182
  • 182
  • Any idea why would `do...end` and `{...}` both are not treated as blocks to `p` as shown in first case of your answer – Wand Maker Jan 06 '16 at 19:12
  • Don't worry, I got answer about it from another [SO thread](http://stackoverflow.com/questions/5587264/do-end-vs-curly-braces-for-blocks-in-ruby). – Wand Maker Jan 06 '16 at 19:18
  • 1
    I can't explain it except to say that it's how the parser works. There may be some technical reason, but I suspect it was simply a design choice. There's a good use case for each style, but it's unfortunate that it catches people off guard sometimes. – Jordan Running Jan 06 '16 at 19:22
0

According to the official documentation website

Returns a new hash consisting of entries for which the block returns true.

If no block is given, an enumerator is returned instead.

Here is the code of the method in C

rb_hash_select(VALUE hash)
{
    VALUE result;

    RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
    result = rb_hash_new();
    if (!RHASH_EMPTY_P(hash)) {
        rb_hash_foreach(hash, select_i, result);
    }
    return result;
}

http://ruby-doc.org/core-2.3.0/Hash.html#method-i-select

Community
  • 1
  • 1
Richard Hamilton
  • 25,478
  • 10
  • 60
  • 87