Skip over iteration in Enumerable#collect
There is method Enumerable#reject
which serves just the purpose:
(1..4).reject{|x| x == 3}.collect{|x| x + 1}
The practice of directly using an output of one method as an input of another is called method chaining and is very common in Ruby.
BTW, map
(or collect
) is used for direct mapping of input enumerable to the output one. If you need to output different number of elements, chances are that you need another method of Enumerable
.
Edit: If you are bothered by the fact that some of the elements are iterated twice, you can use less elegant solution based on inject
(or its similar method named each_with_object
):
(1..4).each_with_object([]){|x,a| a << x + 1 unless x == 3}
Count iteration on the Enumerable cycle
You can put something like that together fairly easily. Something like
class Iter < Array
attr_reader :iteration
def initialize(*args)
super(*args)
@pointer = 0
@iteration = 1 # Current iteration
end
def next
self[@pointer].tap {
@pointer = (@pointer + 1) % size
@iteration += 1 if @pointer == 0
}
end
end
iter = Iter.new [1,2,3]
7.times { puts 'iteration %d: %d' % [iter.iteration, iter.next] }
# iteration 1: 1
# iteration 1: 2
# iteration 1: 3
# iteration 2: 1
# iteration 2: 2
# iteration 2: 3
# iteration 3: 1
Understanding grep within Enumerable module
From the docs of Enumerable#grep
:
grep(pattern) → array
grep(pattern) { |obj| block } → arrayReturns an array of every element in
enum
for whichPattern === element
. If the optional block is supplied, each matching element is passed to it, and the block’s result is stored in the output array.
The important part is this method returns elements that evaluate Pattern === element
to true
. But /M/ === {name: 'John', grade: 8, gender: 'M'}
does not return true
, same for all other elements in your array.
Therefore your result set is empty in the first place.
The block – { |gend| gend[:gender] }
in your example – is only evaluated when and after there was a pattern match. The block changes the return value of the whole call but not how the pattern matching is done.
Please note the docs for Rexgxp#===
in this context too.
Requirements for including Enumerable
From Pickaxe p. 474 and also from the core documentation:
The class [mixing in Enumerable] must provide a method
each
, which
yields successive members of the collection. IfEnumerable#max
,
min
,sort
, orsort_by
is used, the objects in the collection
must also implement a meaningful<=>
operator, because these methods
rely on an ordering between members of a collection.
Map an array modifying only elements matching a certain condition
I agree that the map statement is good as it is. It's clear and simple,, and would easy
for anyone to maintain.
If you want something more complex, how about this?
module Enumerable
def enum_filter(&filter)
FilteredEnumerator.new(self, &filter)
end
alias :on :enum_filter
class FilteredEnumerator
include Enumerable
def initialize(enum, &filter)
@enum, @filter = enum, filter
if enum.respond_to?(:map!)
def self.map!
@enum.map! { |elt| @filter[elt] ? yield(elt) : elt }
end
end
end
def each
@enum.each { |elt| yield(elt) if @filter[elt] }
end
def each_with_index
@enum.each_with_index { |elt,index| yield(elt, index) if @filter[elt] }
end
def map
@enum.map { |elt| @filter[elt] ? yield(elt) : elt }
end
alias :and :enum_filter
def or
FilteredEnumerator.new(@enum) { |elt| @filter[elt] || yield(elt) }
end
end
end
%w{ a b c }.on { |x| x == 'b' }.map { |x| x + "!" } #=> [ 'a', 'b!', 'c' ]
require 'set'
Set.new(%w{ He likes dogs}).on { |x| x.length % 2 == 0 }.map! { |x| x.reverse } #=> #<Set: {"likes", "eH", "sgod"}>
('a'..'z').on { |x| x[0] % 6 == 0 }.or { |x| 'aeiouy'[x] }.to_a.join #=> "aefiloruxy"
Mapping and selecting at the same time
Almost the same to reduce or inject
new_array = some_array.each_with_object([]) do |m,res|
res << modification(x) if some_condition(x)
end
The difference is that you don't need to put result at the end of block.
Requirements for including Enumerable
From Pickaxe p. 474 and also from the core documentation:
The class [mixing in Enumerable] must provide a method
each
, which
yields successive members of the collection. IfEnumerable#max
,
min
,sort
, orsort_by
is used, the objects in the collection
must also implement a meaningful<=>
operator, because these methods
rely on an ordering between members of a collection.
Related Topics
Is There a Literal Notation for an Array of Symbols
What's the Difference Between Process.Fork and Process.Spawn in Ruby 1.9.2
Fastest Way to Check If a String Matches a Regexp in Ruby
In Ruby What Does "=>" Mean and How Does It Work
Heroku Wrongly Detecting My Node App as a Ruby App
Accessing Objects Memory Address in Ruby
Ruby - Iterate Over Parsed JSON
What Command Opens Ruby's Repl
How to Run Rails Console in the Test Environment and Load Test_Helper.Rb
How to Track Down a Memory Leak in My Ruby Code
How to Get Elapsed Time in Milliseconds in Ruby
How to Unfreeze an Object in Ruby
Rails Migration Changing Column to Use Postgres Arrays
Active Admin - Refresh Second Drop Down Based on First Drop Down, Ruby on Rails
How to Wrap the Invocation of a Ruby Method by Including a Module