Josh told me today that one way to see if someone is any good at Ruby is to have them implement the *ect methods in terms of inject. This isn't a fool-proof method, and I'm also not concerned about it's screening value as much as I am simply interested in how to do it. Most importantly, do I meet Josh's standards?

(As Nick pointed out, anyone who's spent more than 5 minutes functional programming would laugh at this. I'm a newbie.)

Before I show my code, I'm going to give you the opportunity to go try it for yourself. Write custom #detect, #select, #reject, and #collect methods in terms of Enumerable#inject.

Here's a little RSpec example that will let you automatically verify your implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
describe Looper do
  before(:each) do
    @looper = Looper.new(1,2,3)
  end
  
  it "should detect" do
    @looper.detect {|i| i > 1 }.should == 2
  end

  it "should select" do
    @looper.select {|i| i % 2 == 1 }.should == [1,3]
  end

  it "should reject" do
    @looper.reject {|i| i % 2 == 1 }.should == [2]
  end

  it "should collect" do
    @looper.collect {|i| i * 2 }.should == [2,4,6]
  end
end

And now for my implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Looper
  def initialize(*args)
    @items = args
  end

  def detect
    @items.inject(nil) {|found, i| found ||= i if yield(i); found }
  end

  def select
    @items.inject([]) {|matches, i| matches << i if yield(i); matches }
  end

  def reject
    @items.inject([]) {|matches, i| matches << i unless yield(i); matches }
  end

  def collect
    @items.inject([]) {|matches, i| matches << yield(i) }
  end
end

How did you do it?

blog comments powered by Disqus