Yes, the with_index
is a bit magic here.
Let me try to show by example what is actually going on:
First lets look at map
. map
iterates through the array and returns the result in the same place. So if we do elem + 1
we should end up with an array where each element have been increased by one.
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
arr.map { |elem| elem + 1 } # => [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
We can now look at select
, which as the name suggests selects the elements and keep those we need. That is, if the block evaluate to true
we keep the element else we discard it.
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
arr.select { |elem| elem == 5 } # => [5]
arr.select { |elem| elem > 7 } # => [8, 9, 10]
reject
is just the opposite of select
. Here we reject, if we evaluate to true
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
arr.reject { |elem| elem == 5 } # => [1, 2, 3, 4, 6, 7, 8, 9, 10]
arr.reject { |elem| elem > 7 } # => [1, 2, 3, 4, 5, 6, 7]
It is important to note that all of these methods return a new array and thus do not change the original.
with_index
works on an enumerator, which map
, select
and reject
(among others) return. So if we call with_index
on the reject
directly, without giving a block to reject
we get the magic with_index
construct. This will call the original enumerator (e.g. select
or reject
) but will yield both the element and the current index.
You can thus think of the with_index
as changing your array into an array of pairs:
arr =[(1,0), (2,1), (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8), (10,9)]
and if we call reject
on these, we get a pair out, thus the two arguments. Since we only care about the index, and not the actual value of the element, we can ignore the first argument, which as a codestyle you show with _
.