What does map(&:name) mean in Ruby?

后端 未结 16 2535
时光取名叫无心
时光取名叫无心 2020-11-21 05:34

I found this code in a RailsCast:

def tag_names
  @tag_names || tags.map(&:name).join(\' \')
end

What does the (&:name)

相关标签:
16条回答
  • 2020-11-21 05:50

    it means

    array.each(&:to_sym.to_proc)
    
    0 讨论(0)
  • 2020-11-21 05:51

    Josh Lee's answer is almost correct except that the equivalent Ruby code should have been as follows.

    class Symbol
      def to_proc
        Proc.new do |receiver|
          receiver.send self
        end
      end
    end
    

    not

    class Symbol
      def to_proc
        Proc.new do |obj, *args|
          obj.send self, *args
        end
      end
    end
    

    With this code, when print [[1,'a'],[2,'b'],[3,'c']].map(&:first) is executed, Ruby splits the first input [1,'a'] into 1 and 'a' to give obj 1 and args* 'a' to cause an error as Fixnum object 1 does not have the method self (which is :first).


    When [[1,'a'],[2,'b'],[3,'c']].map(&:first) is executed;

    1. :first is a Symbol object, so when &:first is given to a map method as a parameter, Symbol#to_proc is invoked.

    2. map sends call message to :first.to_proc with parameter [1,'a'], e.g., :first.to_proc.call([1,'a']) is executed.

    3. to_proc procedure in Symbol class sends a send message to an array object ([1,'a']) with parameter (:first), e.g., [1,'a'].send(:first) is executed.

    4. iterates over the rest of the elements in [[1,'a'],[2,'b'],[3,'c']] object.

    This is the same as executing [[1,'a'],[2,'b'],[3,'c']].map(|e| e.first) expression.

    0 讨论(0)
  • 2020-11-21 05:53

    It basically execute the method call tag.name on each tags in the array.

    It is a simplified ruby shorthand.

    0 讨论(0)
  • 2020-11-21 05:54

    While let us also note that ampersand #to_proc magic can work with any class, not just Symbol. Many Rubyists choose to define #to_proc on Array class:

    class Array
      def to_proc
        proc { |receiver| receiver.send *self }
      end
    end
    
    # And then...
    
    [ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ]
    #=> ["Hello world!", "Goodbye world!"]
    

    Ampersand & works by sending to_proc message on its operand, which, in the above code, is of Array class. And since I defined #to_proc method on Array, the line becomes:

    [ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
    
    0 讨论(0)
  • 2020-11-21 05:54

    Two things are happening here, and it's important to understand both.

    As described in other answers, the Symbol#to_proc method is being called.

    But the reason to_proc is being called on the symbol is because it's being passed to map as a block argument. Placing & in front of an argument in a method call causes it to be passed this way. This is true for any Ruby method, not just map with symbols.

    def some_method(*args, &block)
      puts "args: #{args.inspect}"
      puts "block: #{block.inspect}"
    end
    
    some_method(:whatever)
    # args: [:whatever]
    # block: nil
    
    some_method(&:whatever)
    # args: []
    # block: #<Proc:0x007fd23d010da8>
    
    some_method(&"whatever")
    # TypeError: wrong argument type String (expected Proc)
    # (String doesn't respond to #to_proc)
    

    The Symbol gets converted to a Proc because it's passed in as a block. We can show this by trying to pass a proc to .map without the ampersand:

    arr = %w(apple banana)
    reverse_upcase = proc { |i| i.reverse.upcase }
    reverse_upcase.is_a?(Proc)
    => true
    
    arr.map(reverse_upcase)
    # ArgumentError: wrong number of arguments (1 for 0)
    # (map expects 0 positional arguments and one block argument)
    
    arr.map(&reverse_upcase)
    => ["ELPPA", "ANANAB"]
    

    Even though it doesn't need to be converted, the method won't know how to use it because it expects a block argument. Passing it with & gives .map the block it expects.

    0 讨论(0)
  • 2020-11-21 05:57

    It's equivalent to

    def tag_names
      @tag_names || tags.map { |tag| tag.name }.join(' ')
    end
    
    0 讨论(0)
提交回复
热议问题