Skip Over Iteration in Enumerable#Collect

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 } → array

Returns an array of every element in enum for which Pattern === 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. If Enumerable#max,
min, sort, or sort_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. If Enumerable#max,
min, sort, or sort_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



Leave a reply



Submit