介绍
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
,0
或1
的块,然后使用它来排序数组中的值。
下面是一个例子,可以明确比较数组中的条目以以上升顺序进行排序:
1sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
2sorted_sharks = sharks.sort{|a,b| a <=> b }
3print sorted_sharks
a
和b
变量代表了被比较的数组中的个别元素,结果看起来像这样:
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中使用数据: