Pass block passed to method to another method in Ruby
Asked Answered
H

3

47

I'm trying to write a clone of the ruby keep_if and delete_if array methods. Here is my code.

module Strain
  def keep
    self.inject([]) do |extracts, element|
      yield(element) ? extracts << element : extracts 
    end
  end

  def discard
    self.inject([]) do |extracts, element|
      !yield(element) ? extracts << element : extracts
    end
  end
end

class Array
  include Strain
end

This works. But I want to do something like:

def discard
  self - self.keep &block
end

Desired behaviour:

[1, 2, 3].discard { |number| number < 2 }
# => [2, 3]

So I need to pass the block that is passed to the discard method, to be passed on to the keep method. How do I achieve this?

Hasa answered 17/12, 2013 at 21:49 Comment(0)
W
55

You can reference the block explicitly

def discard(&block)
  self - self.keep(&block)
end

or implicitly

def discard
  self - self.keep(&Proc.new {})
end

In your case, I would suggest the first approach.

Watery answered 17/12, 2013 at 21:56 Comment(2)
Thanks! I ended up solving it without passing the block, but I learned something nonetheless.Hasa
I'm surprised that's all this answer needed. It sounded like a more complicated solution was desired. I'll try to formulate and ask the original way I read the questionAtreus
S
9

In the second example, &Proc.new {} doesn't pass a block, it creates a new empty one. One should omit {} and write it as self.keep(&Proc.new) or just keep(&proc) as self. is redundant and proc is the recommended synonym for Proc.new:

# passes on the block or the absence of a block
def discard(&block)
  self - keep(&block)
end

# passes on the block and fails if no block given
def discard
  self - keep(&proc)
end

Both Proc.new and proc without a block use the block of the current method.

&proc will fail if discard doesn't get a block. So the first example is the best if you want to pass the block or the absence of a block (&nil passes no block at all). The second example (as I changed it) is the best if missing block is an error.

In both cases, each time 'discard' is called, a new 'Proc' object is created, and it's not free.

Starks answered 13/10, 2019 at 9:41 Comment(0)
T
2

March 2024 update:

You can also use Anonymous Block Forwarding, which is available since Ruby 3.1:

def discard(&)
  keep(&)
end
Tortile answered 25/3 at 11:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.