如何在 Ruby 中使用数组方法

简介

数组可以在程序中表示数据列表。一旦您有了数组中的数据,就可以对其进行排序、去除重复项、反转顺序、提取数组的部分或者在数组中搜索特定数据。您还可以将数组转换成字符串,将一个数据数组转换成另一个数组,并将一个数组合并成一个单一的值。

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

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

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

这是Ruby中常用的命名约定。它并不是在程序级别上强制执行的,而只是识别方法可预期行为的另一种方式。

让我们从几种访问元素的方法开始探索数组方法。

访问元素

如果你已经按照Ruby中的数组教程操作过了,你就会知道可以通过索引访问单个元素,索引是从零开始的,就像这样:

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

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

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

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

sharks.fetch(42)
Output

IndexError: 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”,并指定我们想要两个元素,然后我们得到一个包含“Great White”和“Hammerhead”的新数组。

你可以使用切片方法来做相同的事情。

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

切割方法也会返回一个新的数组,不改变原始数组。然而,如果你使用切割!方法,原始数组也会被改变。

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

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

有时候你想从数组中随机抓取一个值,而不是特定的一个。让我们探索一下如何实现。

从数组中获取一个随机条目

也许你正在开发一款赌博游戏,或者你正在编写一个能够选择竞赛获胜者的程序。这些事情都需要某种随机值。一个常见的解决方案就是将可能的选择放入一个数组中,然后随机选择一个索引。

要从数组中获取一个随机元素,你可以生成一个介于0到数组最后一个索引之间的随机索引,并将其用作索引来检索值,但还有一种更简单的方法:sample方法从数组中随机选择一个元素。

让我们用它从一组预设答案中随机获取一个答案,创造一个原始版本的“神奇八球”游戏。

以下是8ball.rb的本土中文词组:

八号球.rb

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

Maybe

样本方法还接受一个参数,该参数会返回一个随机条目的数组。所以,如果你需要多个随机条目,只需提供你想要的数量。

随机鲨鱼.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

然而,包含?需要完全匹配,所以你不能寻找部分单词。

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

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

例如,要找到包含字母a的sharks数组中的第一个条目,可以使用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
Output

Hammerhead

对于数组中的每个元素,find方法执行您提供的代码块。如果代码块中的最后一个表达式评估为真,find方法将返回该值并停止迭代。如果在遍历所有元素后没有找到任何值,它会返回nil。

选择方法的工作方式类似,但不同之处在于它构造一个新数组,其中包含所有满足条件的元素,而不仅仅返回一个单一值并停止处理。

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”]

选择和拒绝都会返回一个新的数组,原始数组保持不变。不过,如果使用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”]

逆转方法返回一个新的数组,并且不修改原始数组。如果你想改变原始数组,可以使用逆转!方法。

然而,反转数组并不总是最有效或实际的排序数据的方法。可以使用排序方法按照所需的方式对数组元素进行排序。

对于简单的字符串数组或数字数组,sort方法是高效的,并且会给你想要的结果。

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

[“Angel”, “Great White”, “Hammerhead”, “Tiger”]

然而,如果你想按不同的方式对事物进行排序,你需要告诉排序方法如何做。排序方法接受一个Ruby块,该块使你能够访问数组中的元素,以便进行比较。

为了进行比较,你使用比较运算符(<=>),通常称为太空船运算符。这个运算符比较两个Ruby对象,并返回-1,如果左边的对象较小;返回0,如果两个对象相等;返回1,如果左边的对象较大。

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

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

以下是一个明确比较数组元素并按升序排序的示例:

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

a和b变量代表数组中进行比较的单个元素。结果如下所示:

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”]

当数组包含简单数据类型(如整数、浮点数和字符串)时,排序方法非常好用。但是,当数组包含更复杂的对象时,你需要做更多的工作。

这是一个散列数组,每个散列代表一条鲨鱼。

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

用sort对这个进行排序并不容易。对数组调用sort方法失败了。

sharks.sort
Output

ArgumentError: comparison of Hash with Hash failed

为了进行比较,我们需要告诉排序函数我们想要比较的内容。因此,我们将比较哈希表中“: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”,因为它更高效。

无论是sort还是sort_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]

在Web应用程序中,map经常用于将数组转换为HTML下拉列表中的元素。这里是一个非常简化的版本,展示了具体实现的方式。

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

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

print options

现在选项数组中的每个鲨鱼都被包裹在 HTML 标签中。

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

使用map会返回一个新的数组,不会修改原始数组。使用map!会修改现有的数组。同时,请记住map有一个叫做collect的别名。在你的代码中应该保持一致,只使用其中一个。

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

将一个数组转换为字符串。

在Ruby中,所有的对象都有一个to_s方法,用于将对象转换为字符串。这就是print语句所使用的方法。考虑到我们的鲨鱼数组:

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
Output

Hammerhead Great White Tiger Whale

如果你想让每个鲨鱼名称用逗号和空格分隔,那么请使用逗号和空格作为分隔符。

鲨鱼加入.rb
sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
result = sharks.join(", ")
print result
Output

Hammerhead, Great White, Tiger, Whale

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

鲨鱼加入.rb
sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
result = sharks.join
print result
Output

HammerheadGreat WhiteTigerWhale

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

map.rb —> 地图.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
Output

6

你可以使用reduce方法来代替。reduce方法遍历数组,并通过对每个元素执行二进制操作来保持一个累加总数。

reduce方法接受一个初始值作为结果,并且带有一个包含两个本地值的block:对结果的引用和对当前元素的引用。在block内部,您可以指定计算最终结果的逻辑。

由于我们想要将数组求和,所以我们会将结果初始化为0,然后在代码块中将当前值添加到结果中。

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

6

如果你打算将结果初始化为0,可以省略参数,只需传递代码块。这样将会自动将结果设置为数组中的第一个值。

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

6

reduce方法还允许您指定一个二元方法,或者一个接受另一个对象作为参数的方法,并在数组的每个条目上执行。然后reduce使用这些结果来创建一个单一的值。

当你在Ruby中写2 + 2时,你实际上是在整数2上调用+方法。

2.+(2) # 4

Ruby使用了一些语法糖,使你可以将其表示为2 + 2。

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

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

6

尽管可以使用reduce来求和列表中的数字,但它还可以用于转换数值。请记住,reduce将数组缩减为单个值。但并没有规定这个单个值不能是另一个数组。

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

我们可以使用reject将非数字值排除掉,然后使用map将剩下的值转换为整数。但是我们也可以一步完成所有操作,使用reduce函数。下面是具体方法。

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

然后将这个值放入数组中,但只有在它不是nil的情况下。

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

转换_value_数组.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 处理数据。

  • How to Work with Strings in Ruby
  • How To Work with Arrays in Ruby
  • Understanding Data Types in Ruby
发表回复 0

Your email address will not be published. Required fields are marked *


广告
将在 10 秒后关闭
bannerAds