What does the `&:symb` do? [duplicate] - ruby

This question already has answers here:
What does map(&:name) mean in Ruby?
(17 answers)
Closed 4 years ago.
I've seen an example of how to sort a string. To sort case insensitively:
str.chars.sort(&:casecmp).join
#=> "ginrSt"
I'm curious about (&:casecmp). I found that for example:
arr.map(&:name)
is shorthand for
arr.map(&:name.to_proc)
which is same with
arr.map{|el| el.name}
I know the & (ampersand) tries to convert symbol to proc, and pass it as a block to a method. I do not understand how this would work for sort method, which is supposed to compare two values. Would it be as follows?
str.chars.sort{|a, b| a.casecmp ;b.casecmp}.join
It wouldn't be helpful since soft needs a block to return an integer and casecmp needs an argument. (Or is it called parameter in that case?) To me, it looks more like this:
str.chars.sort{|a, b| a.casecmp(b)}.join
How does &:casecmp know to take one of |a, b| as a caller and the other one as an argument? I wouldn't guess it that it is an option.

If more than one parameter is passed to your block, the proc created by Symbol#to_proc uses the additional block parameters as parameters to the method call.
http://phrogz.net/symbol-to-proc-with-multiple-arguments
So, what's really happening is, sort(&:casecmp) is converted to:
sort {|a,b| a.casecmp(b) }
because sort takes two parameters.

Related

Ruby code what does "&:" do here [duplicate]

This question already has answers here:
What does map(&:name) mean in Ruby?
(17 answers)
Closed 5 years ago.
def reverse_words(s)
s.split.map(&:reverse).join(' ')
end
This code reverses each word in a sentence. But I do not understand "&:" in the code. Can someone explain that to me?
map expects a code block that takes one argument. What you would normally do is to call reverse on that argument:
map {|elt| elt.reverse }
With the & syntax you can shorten this to
map(&:reverse)
The colon is there to make a symbol out of the name reverse.
The & means that reverse is referencing a function, not a block.
This method assumes that the caller will pass it a String object.
First the method #splits the string on whitespaces into an array (if the string has no whitespaces it creates an array with the string as the only element)
Calls the Array#map method on the new array and passes it a reference to the String#reverse method.
The & tells the map method that the input is a reference to a method and not a standard block

How would make this more elegant [duplicate]

This question already has an answer here:
How can I more elegantly remove duplicate items across all elements of a Ruby Array?
(1 answer)
Closed 8 years ago.
I have this piece of code in an application and was told to make it more elegant but have no idea how to make it better
self.join(" ").split(" ").uniq
Any suggestion will be much appreciated.
self is an array
flat_map(&:split).uniq
flat_map runs a block over an array, and concatenates all the resulting arrays.
flat_map(&:split) is equivalent to calling s.split on every argument, which happens to do the exact same thing as s.split(' '), (unless you redefine $;, but please don't do that).
We don't need self, so we omit it.

Sum array of numbers [duplicate]

This question already has answers here:
How to sum array of numbers in Ruby?
(16 answers)
Closed 8 years ago.
Q: Write a method, sum which takes an array of numbers and returns the sum of the numbers.
A:
def sum(nums)
total = 0
i = 0
while i < nums.count
total += nums[i]
i += 1
end
# return total
total
end
There has to be another way to solve this without using while, right? Anyone know how?
Edit: This is not an exam or test. This is a practice problem provided on github for app academy. They provide the question and answer as an example. I just read however that good programmers don't like to use while or unless, so I was curious if I could learn something to solve this problem a better way. Like with enumerable? (Noob at Ruby here, obviously..)
Also, I would love any walkthrough or methods that I should learn.. This question is also different because I am asking for specific examples using this data.
The usual way of doing that would be this:
def sum(nums) nums.reduce(&:+) end
which is short for something like this:
def sum(nums) nums.reduce(0) { |total, num| total + num } end
I see that Neil posted a similar solution while I was typing this, so I'll just note that reduce and inject are two names for the same method - Ruby has several aliases like this so that people used to different other languages can find what they're looking for. He also left off the &, which is optional when using a named method for reduce/inject, but not in other cases.
Explanation follows.
In Ruby you don't normally use explicit loops (for, while, etc.). Instead you call methods on the collection you're iterating over, and pass them a block of code to execute on each item. Ruby's syntax places the block after the arguments to the method, between either do...end or {...}, so it looks like traditional imperative flow control, but it works differently.
The basic iteration method is each:
[1,2,3].each do |i| puts i end
That calls the block do |i| puts i end three times, passing it 1, then passing it 2, and finally passing it 3. The |i| is a block parameter, which tells Ruby where to put the value(s) passed into the block each time.
But each just throws away the return value of the block calls (in this case, the three nils returned by puts). If you want to do something with those return values, you have to call a different method. For example, map returns an array of the return values:
[1,2,3].map do |i| puts i end
#=> [nil, nil, nil]
That's not very interesting here, but it becomes more useful if the block returns something:
[1,2,3].map do |i| 2*i end
#=> [2,4,6]
If you want to combine the results into a single aggregate return value instead of getting back an array that's the same size as the input, that's when you reach for reduce. In addition to a block, it takes an extra argument, and the block itself is also called with an extra argument. The extra parameter corresponding to this argument is called the "accumulator"; the first time the block is called, it gets the argument originally passed to reduce, but from then on, it gets the return value of the previous call to the block, which is how each block call can pass information along to the next.
That makes reduce more general than map; in fact, you can build map out of reduce by passing in an empty array and having the block add to it:
[1,2,3].reduce([]) do |a,i| a + [2*i] end
#=> [2,4,6]
But since map is already defined, you would normally just use it for that, and only use reduce to do things that are more, well, reductive:
[1,2,3].reduce(0) do |s, i| s + 2*i end
#=> 12
...which is what we're doing in solving your problem.
Neil and I took a couple extra shortcuts. First, if a block does nothing but call a single method on its parameters and return the result, you can get an equivalent block by prefixing &: to the method name. That is, this:
some_array.reduce(x) do |a,b| a.some_method(b) end
can be rewritten more simply as this:
some_array.reduce(x, &:some_method)
and since a + b in Ruby is really just a more-familiar way of writing the method call a.+(b), that means that you can add up numbers by just passing in &:+:
[1,2,3].reduce(0, &:+)
#=> 6
Next, the initial accumulator value for reduce is optional; if you leave it out, then the first time the block is called, it gets the first two elements of the array. So you can leave off the 0:
[1,2,3].reduce(&:+)
#=> 6
Finally, you normally need the & any time you are passing in a block that is not a literal chunk of code. You can turn blocks into Proc objects and store them in variables and in general treat them like any other value, including passing them as regular arguments to method calls. So when you want to use one as the block on a method call instead, you indicate that with the &.
Some methods, including reduce, will also accept a bare Symbol (like :+) and create the Proc/block for you; and Neil took advantage of that fact. But other iterator methods, such as map, don't work that way:
irb(main):001:0> [-1,2,-3].map(:abs)
ArgumentError: wrong number of arguments (1 for 0)
from (irb):1:in `map'
from (irb):1
from /usr/bin/irb:12:in `<main>'
So I just always use the &.
irb(main):002:0> [-1,2,-3].map(&:abs)
#=> [1, 2, 3]
There are lots of good online tutorials for Ruby. For more general information about map/reduce and related concepts, and how to apply them to problem-solving, you should search for introductions to "functional programming", which is called that because it treats "functions" (that is, blocks of executable code, which in Ruby are realized as Proc objects) as values just like numbers and strings, which can be passed around, assigned to variables, etc.
Probably the most idiomatic way of doing this in Ruby is:
nums.inject(:+)
. . . although this basically hides all the working, so it depends what the test is trying to test.
Documentation for Array#inject

How is this "map" working? [duplicate]

This question already has answers here:
What does map(&:name) mean in Ruby?
(17 answers)
Closed 9 years ago.
Could you explain what the code below is doing:
resp = s3.list_buckets
puts resp.buckets.map(&:name)
My question is specific to map. I am not able to understand how map is being used here. Also, what does (&:name) mean?
I referred map documentation. However, I'm not able to correlate it with the map in the code above. Per the documentation, Map should be followed by a {}, but it is followed by a () in the code above.
In perl context, map will work on an array/list and will return a new array/list. So, it seems to be doing something similar here as well, but I cannot decode that.
Any pointers to documentation would be helpful.
map is an alias for collect
map(&:name) is shortcut for map {|x| x.name }
map expects a block. & calls to_proc on the object, and passes it as a block and Symbol has to_proc implemented. Refer docs for more info
& of (&:name) means what follows it should be a Proc object and will be converted to a code block.
Since & expects a Proc object, :name will be converted to a Proc object, this is done by calling :name.to_proc.
After :name is converted to a Proc object, & then converts the resulting Proc object to a code block and gives the code block to map.
In summary, there are totally two type conversions occured, one is converting a symbol to a Proc object, the other is converting a Proc object to a code block.

whats does .map(&:chomp) do, exactly? [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
What do you call the &: operator in Ruby?
I see '.map(&:chomp)' all the time
I know what chomp and map do, but I want to know what &: does and I'd like to know why I can't find it on the web after 30 minutes of googling.....
It's Symbol#to_proc, and it turns the symbol into a proc which attempts to invoke the given method on its argument, returning the result.
x = :reverse.to_proc
x.call("asdf") # "fdsa", like calling "asdf".reverse
In your case, .map(&:chomp) is equivalent to .map { |x| x.chomp }.
If you can't find it by Googling, it's because you're Googling the wrong thing. It's a well-known Ruby idiom.

Resources