Enumerators in Ruby - ruby

I'm having a trouble understanding Enumerators in Ruby.
Please correct me If I'm wrong, o.enum_for(:arg) method is supposed to convert object to Enumerator and every iteration over object o should call arg method?
What confuses me is how this line of code works
[4, 1, 2, 0].enum_for(:count).each_with_index do |elem, index|
elem == index
end
It should count how many elements are equal to their position in the array, and it works. However, I don't understand what's actually going on. Is each_with_index calling count method on every iteration? If someone could explain, it would be great.

From Programming Ruby:
count enum.count {| obj | block } → int
Returns the count of objects in enum that equal obj or for which the
block returns a true value.
So the enumerator visits each element and adds 1 to the count if the block returns true, which in this case means if the element is equal to the array index.
I haven't seen this pattern used much (if at all)—I would be more inclined to:
[4, 1, 2, 0].each_with_index.select { |elem, index| elem == index }.count
EDIT
Lets take a look at the example from your comment:
[4, 1, 2, 0].enum_for(:each_slice, 2).map do |a, b|
a + b
end
each_slice(2) takes the array 2 elements at a time and returns an array for each slice:
[4, 1, 2, 0].each_slice(2).map # => [[4,1], [2,0]]
calling map on the result lets us operate on each sub-array, passing it into a block:
[4, 1, 2, 0].enum_for(:each_slice, 2).map do |a,b|
puts "#{a.inspect} #{b.inspect}"
end
results in
4 1
2 0
a and b get their values by virtue of the block arguments being "splatted":
a, b = *[4, 1]
a # => 4
b # => 1
You could also take the array slice as the argument instead:
[4, 1, 2, 0].enum_for(:each_slice, 2).map {|a| puts "#{a.inspect}"}
[4, 1]
[2, 0]
Which lets you do this:
[4, 1, 2, 0].enum_for(:each_slice, 2).map {|a| a.inject(:+) } #=> [5,2]
Or if you have ActiveSupport (i.e. a Rails app),
[4, 1, 2, 0].enum_for(:each_slice, 2).map {|a| a.sum }
Which seems a lot clearer to me than the original example.

array.count can normally take a block, but on its own, it returns a fixnum, so it can't be chained to .with_index the way some other iterators can (try array.map.with_index {|x,i ... }, etc).
.enum_for(:count) converts it into a enumerator, which allows that chaining to take place. It iterates once over the members of array, and keeps a tally of how many of them equal their indexes. So count is really only being called once, but only after converting the array into something more flexible.

Related

How to remove duplicate contiguous elements in an array?

Given an array such as
arr = [1,1,1,2,2,3,4,1,2]
I wish to replace each contiguous string of equal elements with a single instance of that element. Here the result would be
[1,2,3,4,1,2]
The three 1s are replaced by a single 1 and the two 2's are replaced by a single 2.
How can I do that for an arbitrary array of Ruby objects?
I know you can do it with chunk_while and map, but there might be other ways:
[1,1,1,2,2,3,4,1,2].chunk_while { |e, f| e == f }.map(&:first)
# [1, 2, 3, 4, 1, 2]
With chunk_while you split the array by chunks whenever the block is evaluated to true, for that the block yields two variables, the "element before" and the "element after", that's to say, for every iteration you're going to get this:
[1, 1]
[1, 1]
[1, 2]
[2, 2]
[2, 3]
[3, 4]
[4, 1]
[1, 2]
After applying the logic in the proc, it'll chunk the receiver whenever the first and second yielded elements are equal, and you get:
[[1, 1, 1], [2, 2], [3], [4], [1], [2]]
After that you can map that result to get only one element from each array - there are many ways, but first is enough.
The chunk_while proc can also be shortened to (&:==), leaving as [1,1,1,2,2,3,4,1,2].chunk_while(&:==).map(&:first).
Similarly and just out of curiosity, you can use slice_when and last to save 2 characters: [1,1,1,2,2,3,4,1,2].slice_when(&:!=).map(&:last).
You can use recursive functions to solve this type problems.
I tried different tests. It works.
mylist = [1,1,1,2,2,3,4,1,2] #Output:[1,2,3,4,1,2]
mylist2 = [1,1,1,1,1,1,2,2,2,2,3,3,5,5,5,4,1,2] # Output:[1,2,3,5,4,1,2]
mylist3 = [-1,-1,-2,3,5,5,5,4,3,8,8,8,9,6,0,0,6,5] # Output: [-1,-2,3,5,4,3,8,9,6,0,6,5]
def myfunc(mylist)
mylist.each_with_index do |val, index|
if val == mylist[index+1]
mylist.delete_at(index+1)
myfunc(mylist) # here we call same function again
end
end
return mylist
end
print myfunc(mylist)
puts
print myfunc(mylist2)
puts
print myfunc(mylist3)
arr = [1,1,1,2,2,3,4,1,2]
arr.each do |i|
(0..arr.length()).each do |j|
if arr[j] == arr[j+1]
arr.delete_at(j)
break
end
end
end
print arr
What is happening here is the entire list is being traversed; matching consecutive elements in each iteration.
Everytime a match is found, delete it and move on to the next iteration (break).
In effect, you will be changing the length of the array in each iteration. hence the break to avoid index errors.
The nested looping is added to handle any no. of consecutive duplicates.

Block with two parameters

I found this code by user Hirolau:
def sum_to_n?(a, n)
a.combination(2).find{|x, y| x + y == n}
end
a = [1, 2, 3, 4, 5]
sum_to_n?(a, 9) # => [4, 5]
sum_to_n?(a, 11) # => nil
How can I know when I can send two parameters to a predefined method like find? It's not clear to me because sometimes it doesn't work. Is this something that has been redefined?
If you look at the documentation of Enumerable#find, you see that it accepts only one parameter to the block. The reason why you can send it two, is because Ruby conveniently lets you do this with blocks, based on it's "parallel assignment" structure:
[[1,2,3], [4,5,6]].each {|x,y,z| puts "#{x}#{y}#{z}"}
# 123
# 456
So basically, each yields an array element to the block, and because Ruby block syntax allows "expanding" array elements to their components by providing a list of arguments, it works.
You can find more tricks with block arguments here.
a.combination(2) results in an array of arrays, where each of the sub array consists of 2 elements. So:
a = [1,2,3,4]
a.combination(2)
# => [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
As a result, you are sending one array like [1,2] to find's block, and Ruby performs the parallel assignment to assign 1 to x and 2 to y.
Also see this SO question, which brings other powerful examples of parallel assignment, such as this statement:
a,(b,(c,d)) = [1,[2,[3,4]]]
find does not take two parameters, it takes one. The reason the block in your example takes two parameters is because it is using destruction. The preceding code a.combination(2) gives an array of arrays of two elements, and find iterates over it. Each element (an array of two elements) is passed at a time to the block as its single parameter. However, when you write more parameters than there is, Ruby tries to adjust the parameters by destructing the array. The part:
find{|x, y| x + y == n}
is a shorthand for writing:
find{|(x, y)| x + y == n}
The find function iterates over elements, it takes a single argument, in this case a block (which does take two arguments for a hash):
h = {foo: 5, bar: 6}
result = h.find {|k, v| k == :foo && v == 5}
puts result.inspect #=> [:foo, 5]
The block takes only one argument for arrays though unless you use destructuring.
Update: It seems that it is destructuring in this case.

Use case of each_with_index vs index in Ruby

New to Ruby; When I am working with arrays, is there a difference between Enumerable#each_with_index and Array#index I have been working with a multidimensional array in which I am trying to find the location of a given value. My code is able to pass all the tests when I use either one. Coordinates is the array I am using to hold the location of the given value (in this case 1)
field=[[1,0],[0,0]]
coordinates=[]
field.each_with_index do |item|
if item.index(1)
coordinates.push(field.index(item)).push(item.index(1))
end
end
Thanks in advance for the help.
Let's take a closer look at your code:
field=[[1,0],[0,0]]
coordindates = []
field.each_with_index do |item|
if item.index(1)
coordinates.push(field.index(item)).push(item.index(1))
end
end
Let:
enum = field.each_with_index
#=> #<Enumerator: [[1, 0], [0, 0]]:each_with_index>
As you see this returns an enumerator.
Ruby sees your code like this:
enum.each do |item|
if item.index(1)
coordinates.push(field.index(item)).push(item.index(1))
end
end
The elements of the enumerator will be passed into the block by Enumerator#each, which will call Array#each since the receiver, field is an instance of the class Array.
We can see the elements of enum by converting it to an array:
enum.to_a
#=> [[[1, 0], 0], [[0, 0], 1]]
As you see, it has two elements, each being an array of two elements, the first being an array of two integers and the second being an integer.
We can simulate the operation of each by sending Enumerator#next to enum and assigning the block variables to the value returned by next. As there is but one block variable, item, we have:
item = enum.next
#=> [[1, 0], 0]
That is quite likely neither what you were expecting nor what you wanted.
Next, you invoke Array#index on item:
item.index(1)
#=> nil
index searches the array item for an element that equals 1. If it finds one it returns that element's index in the array. (For example, item.index(0) #=> 1). As neither [1,0] nor 0 equals 1, index returns nil.
Let's rewind (and recreate the enumerator). You need two block variables:
field.each_with_index do |item, index|...
which is the same as:
enum.each do |item, index|...
So now:
item, index = enum.next
#=> [[1, 0], 0]
item #=> [1, 0]
index #=> 0
and
item.index(1)
#=> 0
I will let you take it from here, but let me mention just one more thing. I'm not advocating it, but you could have written:
field.each_with_index do |(first, second), index|...
in which case:
(first, second), index = enum.next
#=> [[1, 0], 0]
first #=> 1
second #=> 0
index #=> 0
See Ruby doc: http://ruby-doc.org/core-2.2.0/Array.html#method-i-index and http://ruby-doc.org/core-2.2.1/Enumerable.html#method-i-each_with_index
index is a method of Array, it detects whether an item exists in a specific Array, return the index if the item exists, and nil if does not exist.
each_with_index is a method of Enumerable mixin, it usually takes 2 arguments, first one is item and the second one is index.
So your sample could be simplified as:
field = [[1, 0], [0, 0]]
coordinates = []
field.each_with_index do |item, index|
item_index = item.index(1)
coordinates << index << item_index if item_index
end
puts coordinates.inspect # => [0, 0]
Note your field.index(item) is just index.
Array#index and Enumerable#each_with_index are not related whatsoever (functionally speaking), one is used to get the index of an object within an Array and the other one is used to walk through a collection.
Actually each_with_index is a mutation of the each method that additionally yields the index of the actual object within the collection. This method is very useful if you need to keep track of the current position of the object you are in. It saves you the trouble of creating and incrementing an additional variable.
For example
['a', 'b', 'c', 'd'].each_with_index do|char, idx|
puts "#{idx}) #{char}"
end
output:
0) a
1) b
2) c
3) d
Without each_with_index
idx = 0
['a', 'b', 'c', 'd'].each do|char|
puts "#{idx}) #{char}"
idx += 1
end
output:
0) a
1) b
2) c
3) d
To find the index of an object within a multidimensional Array you will have to iterate at least through all the rows of the matrix, like this (using each_with_index and index)
def find_position(matrix, obj)
matrix.each_with_index do|row, i|
return [i, j] if j = row.index(obj)
end
return nil
end
for example:
find_position([[2,3],[4,5]], 5) # => [1, 1]
Given that:
fields = [[1, 0], [0, 0]]
Enumerable#each_with_index passes each index as well as each object to the block:
fields.each_with_index do |field, idx|
puts "#{field}, #{idx}"
end
#=> [1, 0], 0
#=> [0, 0], 1
Array#index returns the index of the matching array element:
puts fields.index([1, 0])
#=> 0

How does to_enum(:method) receive its block here?

This code, from an example I found, counts the number of elements in the array which are equal to their index. But how ?
[4, 1, 2, 0].to_enum(:count).each_with_index{|elem, index| elem == index}
I could not have done it only with chaining, and the order of evaluation within the chain is confusing.
What I understand is we're using the overload of Enumerable#count which, if a block is given, counts the number of elements yielding a true value. I see that each_with_index has the logic for whether the item is equal to it's index.
What I don't understand is how each_with_index becomes the block argument of count, or why the each_with_index works as though it was called directly on [4,1,2,0]. If map_with_index existed, I could have done:
[4,1,2,0].map_with_index{ |e,i| e==i ? e : nil}.compact
but help me understand this enumerable-based style please - it's elegant!
Let's start with a simpler example:
[4, 1, 2, 0].count{|elem| elem == 4}
=> 1
So here the count method returns 1 since the block returns true for one element of the array (the first one).
Now let's look at your code. First, Ruby creates an enumerator object when we call to_enum:
[4, 1, 2, 0].to_enum(:count)
=> #<Enumerator: [4, 1, 2, 0]:count>
Here the enumerator is waiting to execute the iteration, using the [4, 1, 2, 0] array and the count method. Enumerators are like a pending iteration, waiting to happen later.
Next, you call the each_with_index method on the enumerator, and provide a block:
...each_with_index{|elem, index| elem == index}
This calls the Enumerator#each_with_index method on the enumerator object you created above. What Enumerator#each_with_index does is start the pending iteration, using the given block. But it also passes an index value to the block, along with the values from the iteration. Since the pending iteration was setup to use the count method, the enumerator will call Array#count. This passes each element from the array back to the enumerator, which passes them into the block along with the index. Finally, Array#count counts up the true values, just like with the simpler example above.
For me the key to understanding this is that you're using the Enumerator#each_with_index method.
The answer is but a click away: the documentation for Enumerator:
Most [Enumerator] methods [but presumably also Kernel#to_enum and Kernel#enum_for] have two forms: a block form where the contents are evaluated for each item in the enumeration, and a non-block form which returns a new Enumerator wrapping the iteration.
It is the second that applies here:
enum = [4, 1, 2, 0].to_enum(:count) # => #<Enumerator: [4, 1, 2, 0]:count>
enum.class # => Enumerator
enum_ewi = enum.each_with_index
# => #<Enumerator: #<Enumerator: [4, 1, 2, 0]:count>:each_with_index>
enum_ewi.class # => Enumerator
enum_ewi.each {|elem, index| elem == index} # => 2
Note in particular irb's return from the third line. It goes on say, "This allows you to chain Enumerators together." and gives map.with_index as an example.
Why stop here?
enum_ewi == enum_ewi.each.each.each # => true
yet_another = enum_ewi.each_with_index
# => #<Enumerator: #<Enumerator: #<Enumerator: [4, 1, 2, 0]:count>:each_with_index>:each_with_index>
yet_another.each_with_index {|e,i| puts "e = #{e}, i = #{i}"}
e = [4, 0], i = 0
e = [1, 1], i = 1
e = [2, 2], i = 2
e = [0, 3], i = 3
yet_another.each_with_index {|e,i| e.first.first == i} # => 2
(Edit 1: replaced example from docs with one pertinent to the question. Edit 2: added "Why stop here?)
Nice answer #Cary.. I'm not exactly sure how the block makes its way through the chain of objects, but despite appearances, the block is being executed by the count method, as in this stack trace, even though its variables are bound to those yielded by each_with_index
enum = [4, 1, 2, 0].to_enum(:count)
enum.each_with_index{|e,i| raise "--" if i==3; puts e; e == i}
4
1
2
RuntimeError: --
from (irb):243:in `block in irb_binding'
from (irb):243:in `count'
from (irb):243:in `each_with_index'
from (irb):243

Iterate every two elements in ruby for loop

How do you create a for loop like
for (int x=0; x<data.length; x+=2)
in ruby? I want to iterate through an array but have my counter increment by two instead of one.
If what you really want is to consume 2 items from an array at a time, check out each_slice.
[1,2,3,4,5,6,7,8,9].each_slice(2) do |a, b|
puts "#{a}, #{b}"
end
# result
1, 2
3, 4
5, 6
7, 8
9,
Ruby's step is your friend:
0.step(data.length, 2).to_a
=> [0, 2, 4, 6]
I'm using to_a to show what values this would return. In real life step is an enumerator, so we'd use it like:
data = [0, 1, 2, 3, 4, 5]
0.step(data.length, 2).each do |i|
puts data[i]
end
Which outputs:
0
2
4
<== a nil
Notice that data contains six elements, so data.length returns 6, but an array is a zero-offset, so the last element would be element #5. We only get three values, plus a nil which would display as an empty line when printed, which would be element #6:
data[6]
=> nil
That's why we don't usually walk arrays and container using outside iterators in Ruby; It's too easy to fall off the end. Instead, use each and similar constructs, which always do the right thing.
To continue to use step and deal with the zero-offset for arrays and containers, you could use:
0.step(data.length - 1, 2)
but I'd still try working with each and other array iterators as a first choice, which #SergioTulentsev was giving as an example.
(0..data.length).step(2) do |x|
puts x
end
This seems like the closest substitute.
Using Range#step:
a = (1..50).to_a
(1..a.size).step(2).map{|i| a[i-1]} # [1, 3, 5, 7, 9 ...

Resources