如何在 Ruby 中使用数组方法

介绍

Arrays允许您在程序中表示数据列表.一旦您在一个数组中有数据,您可以将其排序,删除重复件,扭转其顺序,提取数组的部分,或搜索特定数据的数组。

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

当你通过本教程工作时,你会看到一些方法以呼声点(!)结束。这些方法往往有副作用,如突变原始值或增加例外。

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

这是在整个Ruby中使用的命名惯例,而不是在程序层面被强制执行的东西;它只是确定你可以从方法中期望的另一种方式。

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

访问元素

如果您已经遵循了教程(How To Work with Arrays in Ruby)(https://andsky.com/tech/tutorials/how-to-work-with-arrays-in-ruby),您知道您可以使用其基于零的索引访问单个元素,如下:

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

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

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

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

1sharks.fetch(42)
1[secondary_label Output]
2IndexError: index 42 outside of array bounds: -4...4

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

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

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

回收多元元素

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

如果您指定一个起始索引,然后是您想要的元素数量,您将收到包含这些值的新数组. 例如,您可以像这样从鲨鱼数组中抓取两个中间条目:

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

我们从索引1开始,这就是伟大的白色,我们指定我们想要的2元素,所以我们得到一个包含伟大的白色锤头的新数组。

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

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

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

使用采取方法,您可以从一个数组的开始捕捉指定数量的条目:

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

有时你想从一个数组中获取随机值,而不是特定值。

从一个 Array 获取随机输入

你可能正在开发一个偶然的游戏,或者你正在编写一个选择比赛赢家的程序. 这些类型的事情需要某种随机值。

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

让我们用它来从一系列股票答案中获取随机答案,创建一个神奇8球游戏的原始版本:

1[label 8ball.rb]
2answers = ["Yes", "No", "Maybe", "Ask again later"]
3print answers.sample
1[secondary_label Output]
2Maybe

示例方法还接受一个返回随机输入数组的参数,所以如果您需要多个随机输入,只需输入您想要的数字:

1[label random_sharks.rb]
2sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
3sample = sharks.sample(2)
4print sample
1[secondary_label Output]
2["Whale", "Great White"]

让我们看看如何在下一个数组中找到特定元素。

查找和过滤元素

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

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

1sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
2sharks.include? "Tiger" # true
3
4["a", "b", "c"].include? 2 # false

但是,包括?需要准确的匹配,所以你不能搜索部分单词。

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

查找方法会找到并返回与您指定的条件匹配的数组中的第一个元素。

例如,要识别包含字母a鲨鱼数组中的第一个条目,您可以使用每个方法来比较每个条目,并在找到第一个条目时停止迭代,如下:

1sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
2result = nil
3sharks.each do |shark|
4if sharks.include? "a"
5result = shark
6break
7end
8end

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

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

「Find」執行您為數列中的每個元素提供的區塊。如果區塊中的最後一個表現評估為「true」,則「Find」方法會返回值並停止重複。

选择方法以类似的方式工作,但它构建了一个包含所有符合条件的元素的新数组,而不是只返回一个值并停止。

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

拒绝方法返回一个包含 don’t 符合条件的元素的新数组. 您可以将其视为删除您不想要的元素的过滤器。

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

选择拒绝都返回一个新的数组,不会改变原始数组,但是,如果使用选择!拒绝!方法,原始数组将被更改。

find_all 方法是选择的代名词,但没有find_all! 方法。

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

整理一个 Array

排序数据是一个常见的做法,您可能需要从最小到最大的名称列表或排序数字。

Ruby 数组有一个反向方法,可以扭转数组中的元素的顺序. 如果您有一个已经有组织的数据列表,则反向是扭转元素的快速方法:

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

反向方法返回一个新数组,而不会更改原始数组。

但是,反转数组并不总是最有效或最实用的方式来排序数据. 使用排序方法以您想要的方式排序数组中的元素。

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

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

但是,如果你想以不同的方式排序事物,你会想告诉种类方法如何做到这一点。

要进行比较,您使用 comparison operator (<=>),通常被称为 spaceship operator. 该 operator 比较两个 Ruby 对象,如果左侧的对象较小,则返回 -1,如果左侧的对象较大,则返回 0。

11 <=> 2 # -1
22 <=> 2 # 0
32 <=> 1 # 1

Ruby 的类型方法接受一个必须返回-1,01的块,然后使用它来排序数组中的值。

下面是一个例子,可以明确比较数组中的条目以以上升顺序进行排序:

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

ab变量代表了被比较的数组中的个别元素,结果看起来像这样:

1[secondary_label Output]
2["Angel", "Great White", "Hammerhead", "Tiger"]

若要将鲨鱼排序为相反的顺序,则在比较中逆转对象:

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

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

以下是一系列的哈希,每个哈希代表着鲨鱼:

1sharks = [
2{name: "Hammerhead"},
3{name: "Great white"},
4{name: "Angel"}
5]

将此分类为种类并不容易,在数组上调用种类失败:

1sharks.sort
1[secondary_label Output]
2ArgumentError: comparison of Hash with Hash failed

为了进行比较,我们必须说‘分类’我们想要比较的东西,所以我们将比较哈希中的‘:name’密钥的值:

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

當您使用更複雜的結構時,您可能會選擇「sort_by」方法,該方法使用更高效的分類算法。

1sharks = [
2{name: "Hammerhead"},
3{name: "Great white"},
4{name: "Angel"}
5]
6
7sorted_sharks = sharks.sort_by{|shark| shark[:name] }
8print sorted_sharks
1[secondary_label Output]
2[{:name=>"Angel"}, {:name=>"Great white"}, {:name=>"Hammerhead"}]

sort_by 方法实现了 Schwartzian 转换,一个最适合基于特定密钥的值来比较对象的分类算法,因此,当你比较对象集合时,你会发现自己使用sort_by,因为它更高效。

无论是sort还是sort_by都返回新数组,不影响原始数组. 如果要修改原始数组,请使用sort!sort_by!代替。

除了排序值外,您还可能想摆脱重复。

删除重复元素

有时你会收到具有某些重复性的数据列表. 你可以重复通过数组并过滤重复,但Ruby的uniq方法使这一点更容易。

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

有时,当你合并两个数据集时,你会最终出现重复。

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

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

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

您可以使用uniq来删除重复,但最好避免将它们完全引入。

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

Ruby 数组还支持抽取,这意味着您可以从鲨鱼中抽取new_sharks,以获取只有新值:

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

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

数据转换

地图方法及其名称收集可以转换数组的内容,这意味着它可以在数组中的每个元素上执行操作。

例如,您可以使用地图对数组中的每个输入执行算术,并创建包含新值的新数组:

1numbers = [2,4,6,8]
2
3# square each number
4squared_numbers = numbers.map {|number| number * number}
5
6print squared_numbers

squared_numbers变量是原始数组,方位:

1[4, 16, 36, 64]

「地圖」通常在網頁應用程式中被用來將一個數列轉化為 HTML 放下列表的元素。

1sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
2
3options = sharks.map {|shark| "<option>#{shark}</option>"}
4
5print options

现在选项阵列中每个鲨鱼都被包裹在<option></option> HTML 标签中:

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

地图返回一个新的数组,不改变原始数组。使用地图!将更改现有的数组,并记住地图有一个名为收集的代码。

由于地图返回了一个新的数组,所以该数组可以进一步转换和操纵,甚至转换成一个字符串。

将一个 Array 转换为 String

Ruby中的所有对象都有一个to_s方法,它将对象转换为一个字符串。

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

调用to_s方法将创建此字符串:

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

这对调试非常好,但在真正的程序中并不太有用。

加入方法将一个数组转换为一个字符串,但为您提供更大的控制权,以便您想要组合元素的方式。 加入方法采用一个参数,指定您想要作为分离器使用的字符。

1[label shark_join.rb]
2sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
3result = sharks.join(" ")
4print result
1[secondary_label Output]
2Hammerhead Great White Tiger Whale

如果您希望每个鲨鱼名称分开一个字符号 _和一个空间,请使用一个字符号和一个空间作为您的界限:

1[label shark_join.rb]
2sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
3result = sharks.join(", ")
4print result
1[secondary_label Output]
2Hammerhead, Great White, Tiger, Whale

如果您不指定加入方法的参数,您仍然会收到一个字符串,但它不会有任何界限:

1[label shark_join.rb]
2sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
3result = sharks.join
4print result
1[secondary_label Output]
2HammerheadGreat WhiteTigerWhale

使用合并地图结合是一种快速的方式来将数据的数组转化为输出。 使用地图来转换数据中的每个元素,然后使用合并将整个东西转化为一个可以打印的字符串。 记住我们将我们的鲨鱼数组转化为HTML元素数组的例子吗? 这里又是相同的例子,但这一次我们将使用合并将元素数组转化为具有新线字符作为分离器的字符串:

1[label map.rb]
2sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
3options = sharks.map {|shark| "<option>#{shark}</option>"}
4output = options.join("\n")
5print output
1[secondary_label Output]
2<option>Hammerhead</option>
3<option>Great White</option>
4<option>Tiger</option>
5<option>Whale</option>

不是将数组转换为字符串,您可能希望获得其内容的总和或执行其他类型的转换,从而产生单个值。

将尺寸缩小为单一值

当您使用数据集时,可能需要将数据滚动成一个单一的值,例如总和,其中一种方法是使用变量和每个方法:

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

您可以使用减少方法来做到这一点,而减少方法会重复一个数组,并通过执行每个元素的二进制操作来保持运行总数。

减少方法接受结果的初始值,以及具有两个本地值的区块:对结果的参考和对当前元素的参考. 在区块中,您指定计算最终结果的逻辑。

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

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

如果您打算将结果初始化为0,您可以忽略参数,然后仅通过块,从而自动将结果设置为数组中的第一个值:

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

减少方法还指定一个二进制方法,或在一个对象上接受另一个对象作为其参数的方法,该方法将为数组中的每个输入执行。

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

12.+(2) # 4

Ruby 使用了一些 _syntactic 糖,因此您可以将其表达为2 + 2

减少方法允许您通过将其名称作为一个符号来指定一个二进制方法,这意味着您可以将 :+’ 传递到减少`方法来总结数组:

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

您可以使用减少来做更多的事情,而不仅仅是添加数字列表. 您可以使用它来转换值. 请记住,减少会将一个数组减少为一个值。

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

我们可以使用拒绝来排除非数字值,然后使用地图将剩余值转换为整数,但我们可以通过减少在一个步骤中完成这一切。

然后,在块中,使用整数方法将当前值转换为整数. 如果不能将该值转换为整数,则整数会引出一个例外,您可以捕捉并将分配给该值。

然后拿出值并将其放入数组中,但只有如果它不是

这就是代码的样子,试试这个:

1[label convert_array_of_values.rb]
2values = ["1", "2", "a", "3"]
3integers = values.reduce([]) do |array, current|
4val = Integer(current) rescue nil
5array.push(val) unless val.nil?
6array
7end
8print integers
1[secondary_label Output]
2[1,2,3]

每当你有一个要转换为单个值的元素列表时,你可能可以用减少来解决它。

结论

在本教程中,你使用了几种方法来处理数组. 你抓住了个别元素,通过搜索数组检索了值,排序了元素,并转换了数据,创建了新的数组,字符串和总数。

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

Published At
Categories with 技术
Tagged with
comments powered by Disqus