Ruby中的“map”方法是什么?

276

.map 在以下代码中是什么意思:

params = (0...param_count).map

10
请一次只提出一个问题。map是在可枚举对象上常见的函数式方法,用于转换序列中的值(需考虑特殊情况)。.....可以用来创建范围。此外,请熟悉REPL,您可以在其中自行尝试这些内容! :) - user166390
6
Ruby 的 REPL 是 irb,而 Rails 的 REPL 则是 rails c。REPL 允许你直接在语言 shell 中测试代码。 - Gary
8个回答

457

map 方法接受一个可枚举对象和一个块,并对每个元素运行该块,输出来自块的每个返回值(除非你使用 map!,否则原始对象不会改变):

[1, 2, 3].map { |n| n * n } #=> [1, 4, 9]

ArrayRange 是可枚举类型。使用带块的map返回一个数组。而 map! 则会改变原始数组。

这在哪些情况下有用,map!each 有什么区别?以下是一个示例:

names = ['danil', 'edmund']

# here we map one array to another, convert each element by some rule
names.map! {|name| name.capitalize } # now names contains ['Danil', 'Edmund']

names.each { |name| puts name + ' is a programmer' } # here we just do something with each element
输出结果:
Danil is a programmer
Edmund is a programmer

3
感谢Speransky提供的例子。那么.map和.each有什么不同? - bigpotato
3
啊,我明白了。所以 .map 实际上会改变数组,而 .each 只是循环遍历数组以获取值,同时不会改变原始数组? - bigpotato
25
对于普通读者来说,开头句子将 map 描述成 map! 的做法是有风险的。请问您需要我翻译什么内容呢? - kaleidic
12
要查看map和each之间的区别,请打开IRB窗口并查看以下代码中y和z的结果:y = [1,2,3].each {|x| x + 1}; z = [1,2,3].map {|x| x + 1}。其中,map方法返回一个新的数组,其中包含通过对初始数组中的每个元素应用块中的操作而得出的结果。另一方面,each方法只是迭代原始数组中的每个元素,并将其传递给一个块,但不会返回新数组。 - davej
7
当提供一个块时,'each' 返回调用它的数组(在这个例子中是[1,2,3]);'map' 返回一个由块计算出的值填充的新数组。这可能有所帮助:将变量ary = [1,2,3],并查看它的object_id。然后运行 y = ary.each {|x| x + 1};z = ary.map {|x| x + 1}。现在检查y和z的object_id。y与ary具有相同的object_id(因为each返回了ary),但z具有不同的object_id,因为map返回了一个新数组。 - davej
显示剩余5条评论

71
"

map,连同selecteach是Ruby代码中的重要组成部分。

它允许您在数组的每个对象上运行操作并将它们全部返回到同一位置。例如,可以将数字数组逐个增加一个:

"
[1,2,3].map {|x| x + 1 }
#=> [2,3,4]

如果您可以在数组元素上运行单个方法,则可以使用简写样式执行此操作,如下所示:
  1. To do this with the above example you'd have to do something like this

    class Numeric
      def plusone
        self + 1
      end
    end
    [1,2,3].map(&:plusone)
    #=> [2,3,4]
    
  2. To more simply use the ampersand shortcut technique, let's use a different example:

    ["vanessa", "david", "thomas"].map(&:upcase)
    #=> ["VANESSA", "DAVID", "THOMAS"]
    

在 Ruby 中转换数据通常涉及一系列 map 操作。学习 mapselect,它们是主要库中最有用的 Ruby 方法之一。它们和 each 一样重要。

map 也是 collect 的别名。使用哪个更符合你的概念就用哪个。)

更有帮助的信息:

如果你在运行 eachmapEnumerable 对象中包含一组 Enumerable 元素(哈希、数组),你可以在块管道中声明这些元素,如下所示:

[["audi", "black", 2008], ["bmw", "red", 2014]].each do |make, color, year|
  puts "make: #{make}, color: #{color}, year: #{year}"
end
# Output:
# make: audi, color: black, year: 2008
# make: bmw, color: red, year: 2014

在哈希(也是一个Enumerable对象)的情况下,哈希只是一个元组数组,并带有解释器的特殊指令。第一个“管道参数”是键,第二个是值。
{:make => "audi", :color => "black", :year => 2008}.each do |k,v|
    puts "#{k} is #{v}"
end
#make is audi
#color is black
#year is 2008

回答实际问题:

假设params是一个哈希表,这是遍历它的最佳方式:使用两个块参数而不是一个来捕获哈希表中每个元组的键值对。

params = {"one" => 1, "two" => 2, "three" => 3}
params.each do |k,v|
  puts "#{k}=#{v}"
end
# one=1
# two=2
# three=3

这在irb中对我不起作用。在ruby 2中,我得到了NoMethodError: private method 'plusone' called for 1:Fixnum,而在ruby 1.9/1.8中则是“参数数量错误”。无论如何,我使用了一个lambda:plusone = ->(x) { x + 1 },然后去掉了符号说明符:[1,2,3].map(&plusone) - tjmcewan
1
听起来像是你在放置方法之前在类内部声明了“private”。 - boulder_ruby
是的,完全是这样。除了它不是。(伤心脸)首先它是直接脚本,没有类,其次是在普通的irb中运行。这是你代码的复制粘贴链接:https://gist.github.com/tjmcewan/a7e4feb2976a93a5eef9 - tjmcewan
是的,我在我的代码中刚刚放了一个错误的例子,非常抱歉。请尝试修改后的代码,现在它可以工作了... - boulder_ruby
1
@boulder_ruby 有没有一种方法可以使用普通方法来实现这个功能 - 即不是类方法? - tekknolagi
显示剩余3条评论

12

8

使用 Ruby 2.4,您可以使用transform_values实现相同的功能,该功能从Rails提取到Ruby中。

h = {a: 1, b: 2, c: 3}

h.transform_values { |v| v * 10 }
 #=> {a: 10, b: 20, c: 30}

4

0..param_count 表示“包括 param_count 在内”。 0...param_count 表示“不包括 param_count ”。

Range#map 不返回一个 Enumerable,它实际上将其映射到一个数组中。这与 Range#to_a 相同。


3
它将函数“映射”到Enumerable中的每个项 - 在这种情况下是一个范围。因此,它会针对从0到param_count(不包括 - 你对点很正确)的每个整数调用传递的块一次,并返回包含每个返回值的数组。 这里是Enumerable#map的文档。 它还有一个别名collect

很奇怪,但是 Range#map 实际上会将其转换为数组。 - Pedro Nascimento
1
@PedroNascimento:是啊...那就是我说的吧? - Ry-
抱歉,我不知道 map 本身并不像 each 一样返回一个 Enumerable。我原以为它会这样。 - Pedro Nascimento

3

#each

#each会针对数组中的每个元素运行一个函数。下面这两段代码等价:

x = 10
["zero", "one", "two"].each{|element|
    x++
    puts element
}

x = 10
array = ["zero", "one", "two"]

for i in 0..2
    x++
    puts array[i]
end

#map

#map 函数会将函数应用于数组中的每个元素,返回结果数组。以下两种方式等效:

array = ["zero", "one", "two"]
newArray = array.map{|element| element.capitalize()}

array = ["zero", "one", "two"]

newArray = []
array.each{|element|
    newArray << element.capitalize()
}

#map!

#map!类似于#map,但在原数组上进行修改。以下两种写法等价:

array = ["zero", "one", "two"]
array.map!{|element| element.capitalize()}

array = ["zero", "one", "two"]
array = array.map{|element| element.capitalize()}

2

Map是可枚举模块的一部分,与“collect”非常相似。例如:

  Class Car

    attr_accessor :name, :model, :year

    Def initialize (make, model, year)
      @make, @model, @year = make, model, year
    end

  end

  list = []
  list << Car.new("Honda", "Accord", 2016)
  list << Car.new("Toyota", "Camry", 2015)
  list << Car.new("Nissan", "Altima", 2014)

  p list.map {|p| p.model}

Map提供了通过数组迭代返回由块参数返回的值。


1
map 和 collect 是完全相同的。 - BenKoshy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接