-1

I done a coding challenge in Ruby not too long ago and wanted a better understanding of how the syntax below works, particularly with the last part of the expression (&:first).

def remove_every_other(arr)
  arr.each_slice(2).map(&:first)
end

For some background the task was to take an array and remove every second element from the array.

Tests:

Test.assert_equals(remove_every_other(['Hello', 'Goodbye', 'Hello Again']), #=> ['Hello', 'Hello Again'])
Test.assert_equals(remove_every_other([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), #=> [1, 3, 5, 7, 9])
Test.assert_equals(remove_every_other([[1, 2]]), #=> [[1, 2]])
Test.assert_equals(remove_every_other([['Goodbye'], {'Great': 'Job'}]), #=> [['Goodbye']])
Test.assert_equals(remove_every_other([]), [])
KobbyAdarkwa
  • 181
  • 2
  • 16

1 Answers1

0

& is a reference to a method.
:first is a symbol.
So &:first is simply a reference to the method named first in any object.
So this is the same thing as saying arr.each_slice(2).map {|it| it.first }.

When you map a method to elements of a collection, Ruby will simply call that method on the elements of the collection. In your case, arr.each_slice(2) will return elements of arr two by two (see the doc for each_slice), so if your array is e.g. [1, 2, 3, 4, 5, 6, 7, 8] it is a collection containing [[1, 2], [3, 4], [5, 6], [7, 8]]. Mapping :first on that collection means calling the first method on each element, and [1, 2].first simply returns 1 while [7, 8].first returns 7.

As the map method returns a collection with each element replaced with the result of the call to the method, you'll find yourself with a collection containing the first of each pair. This is how it removes every other element.

Note that if the original collection has an odd number of elements then the last one will be an array of one instead of two (see doc for each_slice), so if the array is [1, 2, 3] the slices are [[1, 2], [3]] and calling .first on each will result in [1, 3].

Jean
  • 10,545
  • 2
  • 31
  • 31
  • Makes sense, going to try and include this anyway I can from now ! =D – KobbyAdarkwa Sep 19 '20 at 09:40
  • @Jean: This question has already been asked and answered over a hundred times on [so], there is really no need to spread the answers even further by adding a 101st one. Remember, the idea behind [so] is to have all information *in one place*, so that the best answer can float to the top based on voting. This doesn't work if answers are spread across 100 different questions. – Jörg W Mittag Sep 19 '20 at 10:31
  • Fair enough, sorry. I did not particularly search for similar questions before answering. – Jean Sep 21 '20 at 05:19