How does Ruby's map method work with (&first)? [duplicate] - ruby

This question already has answers here:
What does map(&:name) mean in Ruby?
(17 answers)
Closed 2 years ago.
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([]), [])

& 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].

Related

What does Ruby block return to?

Good day. I've tried writing this code in ruby
x = [1, 2, 3, 4, 5]
x.each do |a|
a + 1
end
When I type this in irb, I don't understand why does it return
=> [1, 2, 3, 4, 5]
I thought it would return
=> [2, 3, 4, 5, 6] # because of a + 1
each yields the array's elements to the given block (one after another) without modifying the array. At the end, it returns the array, as mentioned in the docs:
[...] passes each successive array element to the block; returns self
You are probably looking for map, which works similar to each but instead of returning self, it ...
[...] returns a new Array whose elements are the return values from the block
Example:
x = [1, 2, 3, 4, 5]
x.map { |a| a + 1 }
#=> [2, 3, 4, 5, 6]
Note that it returns a new array without actually modifying x. There's also map! (with !) which does modify the receiver.

How to use a recursive array

I have array, named a and define it with [1, 2, 3].
Next, I pushed it to itself:
a = [1, 2, 3]
a << a
and the result I get is:
#=> [1, 2, 3, [...]]
When I want to get the last element of array using a.last I get:
a.last
#=> [1, 2, 3, [...]]
#even
a.last.last.last
#=> [1, 2, 3, [...]]
What is going on, when we would push array to itself?
Yes, I understand that this should create a recursive array, but what can we do with it?
In Ruby variables, array elements etc. are object references. So when you do a = [1, 2, 3], there will be an array somewhere in memory and the a variable is a reference to that memory. Now when you do a << a, a[4] will also be a reference to that object. So in effect a now contains a reference to itself.
a = [1, 2, 3]
a << a.dup
a.last
=> [1, 2, 3]
a.last.last
=> 3
Maybe this is what you wanted. This just insert an array [1, 2, 3] as the last item of the a array. In the way you did you put a reference at the end of the a array and this becomes recursive.

What does the "*" mean in this recursive ruby function? [duplicate]

This question already has answers here:
What does the (unary) * operator do in this Ruby code?
(3 answers)
What does the * (star) mean in Ruby? [duplicate]
(1 answer)
Closed 7 years ago.
I read this code that is about quicksort with monkey-patching for the Array class.
class Array
def quicksort
return [] if empty?
pivot = delete_at(rand(size))
left, right = partition(&pivot.method(:>))
return *left.quicksort, pivot, *right.quicksort
end
end
I don't know what the star (*) sign seen at the start of *left.quicksort is. Can't we just use left.quicksort?
The star (in this case) stands for array unpacking. The idea behind it is that you want to get one array with the given elements, instead of array of array, element, array:
left = [1, 2, 3]
pivot = 4
right = [5, 6, 7]
[left, pivot, right] # => [[1, 2, 3], 4, [5, 6, 7]]
[*left, pivot, *right] # => [1, 2, 3, 4, 5, 6, 7]
The * takes a list of arguments and splits them into individual elements.
This allows you to return one un-nested array even when the left and right themselves return an array.
Regarding can't we just use left.quicksort did you give it a try?
def a()
return *[1,2,3], 4, *[5,6]
end
def b()
return [1,2,3], 4, *[5,6]
end
b()
=> [[1, 2, 3], 4, 5, 6]
a()
=> [1, 2, 3, 4, 5, 6]
Without the asterisk, you will have three values returned... the first and last value will be a single array with multiple values
[array1], pivot, [array2]
With the asterisk, the array values will be returned as separate components...
array1_value_1, array1_value_2, array1_value_3, ..., pivot, array2_value_1, array2_value2, array2_value_3, ...

Ruby: Standard recursion patterns

One of the things I commonly get hooked up on in ruby is recursion patterns. For example, suppose I have an array, and that may contain arrays as elements to an unlimited depth. So, for example:
my_array = [1, [2, 3, [4, 5, [6, 7]]]]
I'd like to create a method which can flatten the array into [1, 2, 3, 4, 5, 6, 7].
I'm aware that .flatten would do the job, but this problem is meant as an example of recursion issues I regularly run into - and as such I'm trying to find a more reusable solution.
In short - I'm guessing there's a standard pattern for this sort of thing, but I can't come up with anything particularly elegant. Any ideas appreciated
Recursion is a method, it does not depend on the language. You write the algorithm with two kind of cases in mind: the ones that call the function again (recursion cases) and the ones that break it (base cases). For example, to do a recursive flatten in Ruby:
class Array
def deep_flatten
flat_map do |item|
if item.is_a?(Array)
item.deep_flatten
else
[item]
end
end
end
end
[[[1]], [2, 3], [4, 5, [[6]], 7]].deep_flatten
#=> [1, 2, 3, 4, 5, 6, 7]
Does this help? anyway, a useful pattern shown here is that when you are using recusion on arrays, you usually need flat_map (the functional alternative to each + concat/push).
Well, if you know a bit of C , you just have to visit the docs and click the ruby function to get the C source and it is all there..
http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-flatten
And for this case, here is a Ruby implementation
def flatten values, level=-1
flat = []
values.each do |value|
if level != 0 && value.kind_of?(Array)
flat.concat(flatten(value, level-1))
else
flat << value
end
end
flat
end
p flatten [1, [2, 3, [4, 5, [6, 7]]]]
#=> [1, 2, 3, 4, 5, 6, 7]
Here's an example of a flatten that's written in a tail recursive style.
class Array
# Monkeypatching the flatten class
def flatten(new_arr = [])
self.each do |el|
if el.is_a?(Array)
el.flatten(new_arr)
else
new_arr << el
end
end
new_arr
end
end
p flatten [1, [2, 3, [4, 5, [6, 7]]]]
#=> [1, 2, 3, 4, 5, 6, 7]
ruby
Although it looks like ruby isn't always optimized for tail recursion: Does ruby perform tail call optimization?

Count if there is more than 1 occurrence in a 2d array in Ruby

I've got a sorted array:
array = [[4, 13], [1, 12], [3, 8], [2, 8], [0, 3]]
Which shows me a position (array[n][0]) and the number of occurrences of that position (array[n][1]).
I need to test to see if more than one item in the array has the same number of occurrences as the last item.
I thought I might be able to do it with this:
array.detect {|i| i[1] == array.last[1] }.length
But it returns 2 for the above array, and seems to also return 2 for the following array:
array = [[4, 13], [1, 12], [3, 8], [2, 3], [0, 3]]
When I run it without length it always returns the first occurrence.
Is there a way to get this to count the occurrences?
EDIT:
Sorry, will ask my follow up question in a new question.
Try using find_all instead of detect. detect returns the first match. In your first example, that's array[3], which is another array of length 2. That's why it's returning 2 (it should always be returning 2 or nil for your arrays). find_all will return an array of the matches (instead of the first match itself), and its length will be the value you want.
Here's a clearer way of doing what you want, with none of the associated worries.
array.count{|i| i[1] == array.last[1]}

Resources