如何在Ruby中使用数组方法

来自菜鸟教程
跳转至:导航、​搜索

介绍

Arrays 让您可以表示程序中的数据列表。 在数组中拥有数据后,您可以对其进行排序、删除重复项、反转其顺序、提取数组的各个部分或在数组中搜索特定数据。 您还可以将数组转换为 字符串 ,将一个数据数组转换为另一个数据数组,并将数组汇总为单个值。

在本教程中,您将探索 Ruby 为处理存储在数组中的数据提供的一些最实用的方法。

在学习本教程时,您会看到一些以感叹号结尾的方法 (!)。 这些方法通常有副作用,例如改变原始值或引发异常。 您将在本教程中使用的许多方法都有一个带有此后缀的相关方法。

您还会遇到以问号 (?) 结尾的方法。 这些方法返回一个布尔值。

这些是整个 Ruby 中使用的命名约定。 这不是在程序级别强制执行的; 这只是另一种确定您可以从该方法中获得什么的方法。

让我们通过查看访问元素的几种方法开始我们对数组方法的探索

访问元素

如果您已经学习过教程 How To Work with Arrays in Ruby,您就会知道可以使用从零开始的索引来访问单个元素,如下所示:

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sharks[0]    # "Tiger"
sharks[1]    # "Great White"
sharks[-1]   # "Angel"

您可能还记得您可以使用 firstlast 方法来获取数组的第一个和最后一个元素:

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sharks.first   # "Tiger"
sharks.last    # "Angel"

最后,当你访问一个不存在的元素时,你会得到 nil。 但是,如果您想得到一个错误,请使用 fetch 方法:

sharks.fetch(42)
OutputIndexError: index 42 outside of array bounds: -4...4

如果您宁愿指定自己的默认值而不是引发错误,也可以这样做:

sharks.fetch(42, "Nope")     # "Nope"

现在让我们看看如何从一个数组中获取多个元素。

检索多个元素

有时您可能希望从数组中获取值的子集,而不仅仅是单个元素。

如果您指定起始索引,后跟所需元素的数量,您将获得一个包含这些值的新数组。 例如,您可以像这样从 sharks 数组中获取两个中间条目:

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sharks[1,2]   # ["Great White", "Hammerhead"] 

我们从索引 1 开始,即 "Great White",并指定我们想要 2 元素,因此我们得到一个包含 "Great White""Hammerhead"

您可以使用 slice 方法来做同样的事情:

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sharks.slice(1,2)   # ["Great White", "Hammerhead"] 

slice 方法还返回一个新数组,而原始数组保持不变。 但是,如果使用 slice! 方法,原始数组也会被更改。

take 方法允许您从数组的开头获取指定数量的条目:

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sharks.take(2)  # ["Tiger", "Great White"]

有时您想从数组中获取随机值而不是特定值。 让我们探索一下。

从数组中获取随机条目

您可能正在研究机会游戏,或者您正在编写一个选择比赛获胜者的程序。 这些东西需要某种随机值。 一个常见的解决方案是将可能的选择放在一个数组中并选择一个随机索引。

要从数组中获取随机元素,您可以在 0 和数组的最后一个索引之间生成一个随机索引,并将其用作检索值的索引,但有一种更简单的方法:sample 方法从数组中抓取一个随机条目。

让我们用它从一系列股票答案中随机抽取一个答案,创建一个原始版本的 Magic 8-Ball 游戏:

8ball.rb

answers = ["Yes", "No", "Maybe", "Ask again later"]
print answers.sample
OutputMaybe

sample 方法还接受一个返回随机条目数组的参数,因此如果您碰巧需要多个随机条目,只需提供您想要的数字:

random_sharks.rb

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
sample = sharks.sample(2)
print sample
Output["Whale", "Great White"]

接下来让我们看看如何在数组中查找特定元素。

查找和过滤元素

当您在数组中查找特定元素时,您通常会遍历其元素,直到找到您要查找的内容。 但是 Ruby 数组提供了几种专门用于简化数组搜索过程的方法。

如果只想查看元素是否存在,可以使用 include? 方法,如果指定的数据是数组的元素,则返回 true

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
sharks.include? "Tiger"      # true

["a", "b", "c"].include? 2   # false

但是,include? 需要完全匹配,因此您不能查找部分单词。

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
sharks.include? "Tiger"      # true
sharks.include? "tiger"      # false
sharks.include? "ti"         # false

find 方法定位并返回数组中与您指定的条件匹配的第一个元素。

例如,要识别 sharks 数组中包含字母 a 的第一个条目,您可以使用 each 方法比较每个条目并在找到第一个,像这样:

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
result = nil
sharks.each do |shark|
  if sharks.include? "a"
    result = shark
    break
  end
end

或者你可以使用 find 方法来做同样的事情:

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
result = sharks.find {|item| item.include?("a")}
print result
OutputHammerhead

find 执行您为数组中的每个元素提供的块。 如果块中的最后一个表达式的计算结果为 true,则 find 方法返回该值并停止迭代。 如果在遍历所有元素后没有找到任何内容,则返回 nil

select 方法以类似的方式工作,但它构造一个包含所有匹配条件的元素的新数组,而不是仅返回单个值并停止。

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
results = sharks.select {|item| item.include?("a")}
print results
Output["Hammerhead", "Great White", "Whale"]

reject 方法返回一个新数组,其中包含 匹配条件的元素。 您可以将其视为删除您不想要的元素的过滤器。 这是一个拒绝所有包含字母 a 的条目的示例:

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
results = sharks.reject {|item| item.include?("a")}
print results
Output["Tiger"]

selectreject 都返回一个新数组,而原始数组保持不变。 但是,如果使用 select!reject! 方法,则会修改原始数组。

find_all 方法是 select 的别名,但没有 find_all! 方法。

接下来,让我们看看如何对数组的值进行排序。

对数组进行排序

对数据进行排序是一种常见的做法。 您可能需要按字母顺序排列姓名列表或将数字从小到大排序。

Ruby 数组有一个 reverse 方法,可以反转数组中元素的顺序。 如果您有一个已经组织好的数据列表,reverse 是一种快速翻转元素的方法:

sharks = ["Angel", "Great White", "Hammerhead", "Tiger"]
reversed_sharks = sharks.reverse
print reversed_sharks
Output["Tiger", "Hammerhead", "Great White", "Angel"]

reverse 方法返回一个新数组并且不修改原始数组。 如果要更改原始数组,请使用 reverse! 方法。

但是,反转数组并不总是最有效或最实用的数据排序方式。 使用 sort 方法按您喜欢的方式对数组中的元素进行排序。

对于简单的字符串或数字数组,sort 方法是有效的,并且会给你你正在寻找的结果:

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sorted_sharks = sharks.sort
print sorted_sharks
Output["Angel", "Great White", "Hammerhead", "Tiger"]

但是,如果您想以不同的方式对事物进行排序,则需要告诉 sort 方法如何执行此操作。 sort 方法采用一个 Ruby 块,可让您访问数组中的元素,以便比较它们。

要进行比较,请使用 比较运算符 (<=>),通常称为 宇宙飞船运算符 。 此运算符比较两个 Ruby 对象,如果左边的对象较小,则返回 -1,如果对象相同,则返回 0,如果左边的对象较小,则返回 1大。

1 <=> 2    # -1
2 <=> 2    #  0
2 <=> 1    #  1

Ruby 的 sort 方法接受一个必须返回 -101 的块,然后使用它对数组中的值进行排序。

这是一个显式比较数组中的条目以按升序排序的示例:

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sorted_sharks = sharks.sort{|a,b| a <=> b }
print sorted_sharks

ab 变量表示数组中被比较的单个元素。 结果如下所示:

Output["Angel", "Great White", "Hammerhead", "Tiger"]

要以相反的顺序对鲨鱼进行排序,请反转比较中的对象:

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sorted_sharks = sharks.sort{|a,b| b <=> a }
print sorted_sharks
Output["Tiger", "Hammerhead", "Great White", "Angel"]

sort 方法非常适合包含简单数据类型(如整数、浮点数和字符串)的数组。 但是当数组包含更复杂的对象时,您将不得不做更多的工作。

这是一个哈希数组,每个哈希代表一条鲨鱼:

sharks = [
  {name: "Hammerhead"},
  {name: "Great white"},
  {name: "Angel"}
]

使用 sort 对其进行排序并不容易。 在阵列上调用 sort 失败:

sharks.sort
OutputArgumentError: comparison of Hash with Hash failed

为了进行比较,我们必须告诉 sort 我们要比较什么。 所以我们将比较哈希中 :name 键的值:

sorted_sharks.sort{|a, b| a[:name] <=> b[:name]}
print sorted_sharks
Output[{:name=>"Angel"}, {:name=>"Great white"}, {:name=>"Hammerhead"}]

当您使用更复杂的结构时,您可能希望查看 sort_by 方法,它使用更有效的排序算法。 sort_by 接受一个只需要一个参数的块,即对数组中当前元素的引用:

sharks = [
  {name: "Hammerhead"},
  {name: "Great white"},
  {name: "Angel"}
]

sorted_sharks = sharks.sort_by{|shark| shark[:name] }
print sorted_sharks
Output[{:name=>"Angel"}, {:name=>"Great white"}, {:name=>"Hammerhead"}]

sort_by 方法实现了 Schwartzian 变换,这是一种最适合基于特定键值比较对象的排序算法。 因此,您会发现自己在比较对象集合时使用 sort_by,因为它更有效。

sortsort_by 都返回新数组,而原始数组保持不变。 如果要修改原始数组,请改用 sort!sort_by!

除了对值进行排序之外,您可能还想去除重复值。

删除重复元素

有时你会得到一些重复的数据列表。 您可以遍历数组并过滤掉重复项,但是 Ruby 的 uniq 方法使这变得容易得多。 uniq 方法返回一个删除了所有重复值的新数组。

[1,2,3,4,1,5,3].uniq   # [1,2,3,4,5]

有时,当您合并两组数据时,最终会出现重复。 以这两组鲨鱼为例:

sharks = ["Tiger", "Great White"]
new_sharks = ["Tiger", "Hammerhead"]

如果我们将它们加在一起,我们将得到一个重复的条目:

sharks + new_sharks
# ["Tiger", "Great White", "Tiger", "Hammerhead"]

您可以使用 uniq 删除重复项,但最好避免完全引入它们。 不用将数组加在一起,而是使用管道运算符|,它将数组合并在一起:

sharks | new_sharks
# ["Tiger", "Great White", "Hammerhead"]

Ruby 数组也支持减法,这意味着您可以从 sharks 中减去 new_sharks 以仅获得新值:

sharks = ["Tiger", "Great White"]
new_sharks = ["Tiger", "Hammerhead"]
sharks - new_sharks   # ["Great White"]

接下来,让我们看看如何操作每个元素的值。

转换数据

map 方法及其别名 collect 可以转换数组的内容,这意味着它可以对数组中的每个元素执行操作。

例如,您可以使用 map 对数组中的每个条目执行算术运算,并创建一个包含新值的新数组:

numbers = [2,4,6,8]

# square each number
squared_numbers = numbers.map {|number| number * number}

print squared_numbers

squared_numbers 变量是原始数字的平方数组:

[4, 16, 36, 64]

map 通常在 Web 应用程序中用于将数组转换为 HTML 下拉列表的元素。 这是一个非常简化的版本:

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]

options = sharks.map {|shark| "<option>#{shark}</option>"}

print options

options 数组现在将每个鲨鱼包装在 <option></option> HTML 标记中:

["<option>Hammerhead</option>", "<option>Great White</option>", "<option>Tiger</option>", "<option>Whale</option>"]

map 返回一个新数组,保持原始数组不变。 使用 map! 将修改现有数组。 请记住,map 有一个名为 collect 的别名。 您应该保持一致并在代码中使用其中一种。

由于 map 返回一个新数组,因此可以进一步转换和操作该数组,甚至可以将其转换为字符串。 接下来让我们看看。

将数组转换为字符串

Ruby 中的所有对象都有一个 to_s 方法,该方法将对象转换为字符串。 这就是 print 语句使用的内容。 给定我们的 sharks 数组:

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]

调用 to_s 方法会创建这个字符串:

"[\"Hammerhead\", \"Great White\", \"Tiger\", \"Whale\"]"

这对于调试非常有用,但在实际程序中并不是很有用。

join 方法将数组转换为字符串,但可以让您更好地控制元素的组合方式。 join 方法接受一个参数,该参数指定要用作分隔符的字符。 要将鲨鱼数组转换为由空格分隔的鲨鱼名称字符串,您可以执行以下操作:

鲨鱼加入.rb

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
result = sharks.join(" ")
print result
OutputHammerhead Great White Tiger Whale

如果您希望每个鲨鱼名称用逗号 分隔,请使用逗号和空格作为分隔符:

鲨鱼加入.rb

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
result = sharks.join(", ")
print result
OutputHammerhead, Great White, Tiger, Whale

如果您没有为 join 方法指定参数,您仍然会得到一个字符串,但它不会有任何分隔符:

鲨鱼加入.rb

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
result = sharks.join
print result
OutputHammerheadGreat WhiteTigerWhale

joinmap 结合使用是一种将数据数组转换为输出的快速方法。 使用 map 转换数据中的每个元素,然后使用 join 将整个数据转换为可以打印的字符串。 还记得我们将 sharks 数组转换为 HTML 元素数组的示例吗? 这里还是同样的例子,但这次我们将使用 join 将元素数组转换为以换行符作为分隔符的字符串:

地图.rb

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
options = sharks.map {|shark| "<option>#{shark}</option>"}
output = options.join("\n")
print output
Output<option>Hammerhead</option>
<option>Great White</option>
<option>Tiger</option>
<option>Whale</option>

您可能不想将数组转换为字符串,而是想要获取其内容的总和或执行某种其他类型的转换以产生单个值。 接下来就是这样了。

将数组简化为单个值

在处理一组数据时,您可能会发现需要将数据汇总为单个值,例如总和。 您可以这样做的一种方法是使用变量和 each 方法:

result = 0
[1, 2, 3].each {|num| result += num}
print result
Output6

您可以使用 reduce 方法来代替。 reduce 方法遍历数组并通过对每个元素执行二元运算来保持运行总计。

reduce 方法接受结果的初始值,以及具有两个局部值的块:对结果的引用和对当前元素的引用。 在块内部,您指定计算最终结果的逻辑。

由于我们想要对数组求和,我们将结果初始化为 0,然后将当前值添加到块中的结果中:

output = [1,2,3].reduce(0) {|result, current| result += current }
print output
Output6

如果您打算将结果初始化为 0,则可以省略参数并仅传递块。 这将自动将结果设置为数组中的第一个值:

output = [1,2,3].reduce {|result, current| result += current }
print output
Output6

reduce 方法还可以指定一个 二进制方法,或一个对象上的一个方法,该方法接受另一个对象作为其参数,它将为数组中的每个条目执行。 reduce 然后使用结果创建单个值。

当您在 Ruby 中编写 2 + 2 时,实际上是在对整数 2 调用 + 方法:

2.+(2)   # 4

Ruby 使用了一些 语法糖 ,因此您可以将其表示为 2 + 2

reduce 方法允许您通过将其名称作为符号传递来指定二进制方法。 这意味着您可以将 :+ 传递给 reduce 方法来对数组求和:

output = [1, 2, 3].reduce(:+)   
print output
Output6

您可以使用 reduce 来做更多的事情,而不仅仅是添加数字列表。 您可以使用它来转换值。 请记住 reduce 将数组简化为单个值。 但是没有规则说单个值不能是另一个数组。

假设我们有一个需要转换为整数的值列表。 但我们只想要可以转换为整数的值。

我们可以使用 reject 丢弃非数字值,然后使用 map 将剩余值转换为整数。 但是我们可以使用 reduce 一步完成。 就是这样。

使用空数组作为初始化值。 然后,在块中,使用 Integer 方法将当前值转换为整数。 如果该值无法转换为整数,Integer 将引发异常,您可以捕获该异常并将 nil 分配给该值。

然后取值并将其放入数组中,但前提是它不是 nil

这是代码的样子。 试试这个:

convert_array_of_values.rb

values = ["1", "2", "a", "3"]
integers = values.reduce([]) do |array, current|
  val = Integer(current) rescue nil
  array.push(val) unless val.nil?
  array
end
print integers
Output[1,2,3]

每当您有一个需要转换为单个值的元素列表时,您都可以使用 reduce 来解决它。

结论

在本教程中,您使用了几种方法来处理数组。 您抓取单个元素,通过搜索数组检索值,对元素进行排序,然后转换数据,创建新数组、字符串和总计。 您可以应用这些概念来解决许多常见的 Ruby 编程问题。

请务必查看这些相关教程,以继续探索如何在 Ruby 中处理数据: