Reputation: 22225
I needed a function which is similar to Array#select
, but passes to the block not only the data item, but also the index of the element (similar to Enumerable#each_with_index
). I tried this:
class Array
def select_with_index
self.each_with_index.select {|*args| yield(*args)}.map(&:first)
end
end
This seems to work indeed:
['a','b','a','a','c'].select_with_index {|letter,index| letter == 'a' && index > 1 }
results in
["a", "a"]
as expected. However, what I don't like with my solution is, that one has to supply a block. Similar methods in the Ruby core can be called without a block and yield an Enumerator
. How can I do this? I know that I can use block_given?
to test for the presence of the block, but how do I continue then? Do I need a Fiber
?
BTW, the code should work with both Ruby 1.9.3. and 2.x.
Upvotes: 2
Views: 148
Reputation: 230296
You don't need your own method. You can leverage the fact that select
returns an enumerator if called without a block, so you can just slap a with_index
on top:
p ['a','b','a','a','c'].select.with_index {|letter,index| letter == 'a' && index > 1 }
# >> ["a", "a"]
If you really want this to be a method on array (for dynamic invocation and whatnot), this is trivial:
class Array
def select_with_index(&block)
select.with_index(&block)
end
end
p ['a','b','a','a','c'].select_with_index {|letter,index| letter == 'a' && index > 1 }
p ['a','b','a','a','c'].select_with_index
# >> ["a", "a"]
# >> #<Enumerator: #<Enumerator: ["a", "b", "a", "a", "c"]:select>:with_index>
Upvotes: 2
Reputation: 121000
class Array
def select_with_index
# ⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓ this
return enum_for(:select_with_index) unless block_given?
each_with_index.select {|*args| yield(*args)}.map(&:first)
end
end
Upvotes: 3