Skip to content

Ruby 数组

Ruby 数组是功能强大、有序的集合,可以包含任何类型的对象,包括数字、字符串、hash,甚至其他数组。数组中的每个元素都通过基于整数的索引来访问。

数组索引从 0 开始,类似于 C 或 Java 等语言。负数索引从数组末尾开始计数:-1 指最后一个元素,-2 指倒数第二个,依此类推。Ruby 数组是动态的,这意味着当你添加或删除元素时,它们会自动增长或缩小。

创建数组最常见的方式是使用数组字面量语法 []:

# 一个空数组
empty_array = []
# 一个数字数组
numbers = [1, 2, 3, 4, 5]
# 一个混合类型数组
mixed_array = [1, "hello", :symbol, ["nested", "array"]]

你也可以使用 Array.new 类方法:

#!/usr/bin/ruby
# 创建一个空数组
names = Array.new
# 创建一个具有预定义大小(20 个元素,全部为 nil)的数组
names_with_size = Array.new(20)
# 返回数组的大小(或长度)
puts names_with_size.size # 输出: 20
puts names_with_size.length # 输出: 20

你可以用一个默认值初始化数组的所有元素:

#!/usr/bin/ruby
default_values_array = Array.new(4, "default")
puts default_values_array.inspect # 输出: ["default", "default", "default", "default"]

使用可变对象进行初始化时要小心。所有元素将引用同一个对象:

#!/usr/bin/ruby
# 如果你想要不同的可变对象,这种方式是不正确的
array_of_hashes = Array.new(3, {})
array_of_hashes[0][:name] = "Alice"
puts array_of_hashes.inspect # 输出: [{:name=>"Alice"}, {:name=>"Alice"}, {:name=>"Alice"}]
# 使用块来获取不同的可变对象的正确方式
distinct_hashes_array = Array.new(3) { {} }
distinct_hashes_array[0][:name] = "Alice"
puts distinct_hashes_array.inspect # 输出: [{:name=>"Alice"}, {}, {}]

你也可以使用带块的 new 方法来动态填充每个元素:

#!/usr/bin/ruby
squares = Array.new(5) { |index| index * index }
puts squares.inspect # 输出: [0, 1, 4, 9, 16]

创建数组的其他方式包括 Array.[] 或 Kernel#Array 方法:

nums1 = Array.[](1, 2, 3, 4, 5)
nums2 = Array[1, 2, 3, 4, 5]
# 使用 Kernel#Array 和 range
digits = Array(0..9)
puts digits.inspect # 输出: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

数组自带一套丰富的内置方法,用于操作、迭代和查询。下面是一个例子:

#!/usr/bin/ruby
digits = Array(0..9) # 创建 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
element_at_index_6 = digits.at(6)
puts "Element at index 6: #{element_at_index_6}" # 输出: Element at index 6: 6
first_three = digits.first(3)
puts "First three elements: #{first_three.inspect}" # 输出: First three elements: [0, 1, 2]
digits.push(10) # 在末尾添加 10
puts "After push(10): #{digits.inspect}" # 输出: After push(10): [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
last_element = digits.pop # 删除并返回最后一个元素
puts "Popped element: #{last_element}" # 输出: Popped element: 10
puts "After pop: #{digits.inspect}" # 输出: After pop: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

下表是常用的公共数组方法(假设 array 是一个 Array 对象)。有关完整列表,请务必查阅官方 Ruby 文档。

序号方法及其描述
1array & other_array (交集)
返回一个包含两个数组共有元素的、无重复的新数组。array.intersection(other_array) 是其别名。
2array * int (重复) 或 array * str (连接)
如果 int 是一个整数,返回一个由 array 重复 int 次连接而成的新数组。如果 str 是一个字符串,则等同于 self.join(str)。
3array + other_array (连接)
通过连接两个数组返回一个新数组。不修改原始数组。
4array - other_array (差集)
返回一个新数组,它是原始 array 的副本,移除其中在 other_array 中出现的任何项。array.difference(other_array) 是其别名。
5array <=> other_array (比较)
逐元素比较两个数组。如果 array 分别小于、等于或大于 other_array,则返回 -1、0 或 1。
6array | other_array (并集)
通过将 array 与 other_array 合并,并移除重复项,返回一个新数组。array.union(other_array) 是其别名。
7array << obj (追加/推入)
将给定对象 obj 推入 array 的末尾。就地修改 array 并返回 array 本身,允许链式追加。
8array == other_array (相等性)
如果两个数组包含相同数量的元素且每个对应的元素都相等(使用 == 检查),则返回 true。
9array[index]、array[start, length]、array[range]、array.slice(...) (元素访问/切片)
返回一个元素或一个子数组。负数索引从末尾计数。如果索引超出范围,返回 nil。
10array[index] = obj、array[start, length] = new_subarray、array[range] = new_subarray (元素赋值)
为元素赋值新值或替换子数组。如果索引超出边界,数组会自动增长。
11array.at(index)
返回位于 index 的元素。负数索引从末尾计数。如果索引超出范围,返回 nil。
12array.clear
移除 array 中的所有元素,使其变空。就地修改 array。
13array.collect { |item| block } 或 array.map { |item| block } (转换)
对 array 的每个元素调用一次 block。创建一个包含块返回值的新数组。不修改原始数组。
14array.collect! { |item| block } 或 array.map! { |item| block } (就地转换)
对 array 的每个元素调用一次 block,将元素替换为块返回的值。就地修改 array。
15array.compact
返回一个移除了所有 nil 元素的新数组。不修改原始数组。
16array.compact!
就地从 array 中移除 nil 元素。如果未做任何更改,返回 nil。
17array.concat(other_array, ...)
将 other_array(s) 的元素追加到 array。就地修改 array。
18array.delete(obj) { |obj| block }
从 array 中删除所有等于 obj 的项。如果提供了可选的 block,则在未找到 obj 时执行。就地修改 array。
19array.delete_at(index)
删除指定 index 处的元素,并返回该元素;如果索引超出范围,则返回 nil。就地修改 array。
20array.delete_if { |item| block } 或 array.reject! { |item| block }
删除 array 中 block 求值为 true 的每个元素。就地修改 array。
21array.each { |item| block } (迭代)
对 array 中的每个元素调用一次 block,将该元素作为参数传递。这是迭代数组最常用的方法之一。
22array.each_index { |index| block }
与 Array#each 相同,但传递元素的 index 而非元素本身。
23array.empty?
如果 array 不包含任何元素,则返回 true,否则返回 false。
24array.fetch(index)、array.fetch(index, default)、array.fetch(index) { |index| block }
返回位于 index 的元素。如果 index 超出范围,则根据使用的形式,抛出 IndexError 异常、返回 default 或执行 block。
25array.fill(obj)、array.fill(obj, start [, length])、array.fill(obj, range) 或带块的形式
将 array 中选定的元素设置为 obj 或设置为 block 的结果。就地修改 array。
26array.first 或 array.first(n)
返回 array 的第一个元素,或前 n 个元素。如果 array 为空,第一种形式返回 nil,第二种形式返回一个空数组。
27array.flatten
返回一个将 array 展平为一维的新数组(递归)。
28array.flatten!
就地展平 array。如果没有进行任何修改(即 array 不包含子数组),则返回 nil。
29array.include?(obj)
如果 obj 存在于 array 中(使用 == 检查),则返回 true,否则返回 false。
30array.index(obj) 或 array.find_index(obj)
返回 array 中第一个等于 obj 的元素的索引。如果未找到匹配项,则返回 nil。
31array.insert(index, obj...)
将给定的 obj(s) 插入到给定 index 处的元素之前。就地修改 array。
32array.join(separator = $,)
返回一个字符串,该字符串通过将 array 的每个元素转换为字符串,并用 separator 分隔而成(如果 $, 为 nil,默认分隔符为空字符串)。
33array.last 或 array.last(n)
返回 array 的最后一个或最后 n 个元素。如果 array 为空,第一种形式返回 nil。
34array.length 或 array.size
返回 array 中的元素数量。
35array.pop(n=1)
从 array 中移除最后的 n 个元素并返回它们(如果 n=1 则返回单个元素)。如果 array 为空,返回 nil 或一个空数组。就地修改 array。
36array.push(obj, ...) 或 array.append(obj, ...)
将给定的 obj(s) 追加到 array 的末尾。就地修改 array 并返回 array 本身。
37array.reject { |item| block }
返回一个新数组,包含 array 中 block 求值为 false 的项。不修改原始数组。
38array.reverse
返回一个包含 array 元素逆序的新数组。
39array.reverse!
就地反转 array。
40array.select { |item| block } 或 array.filter { |item| block } (过滤)
对每个元素调用 block。返回一个新数组,包含 block 返回真值的那些元素。
41array.select! { |item| block } 或 array.filter! { |item| block } (就地过滤)
删除 array 中 block 求值为 false 的元素。就地修改 array。
42array.shift(n=1)
从 array 中移除前 n 个元素并返回它们。就地修改 array。
43array.sort 或 array.sort { |a, b| block }
返回一个通过排序 array 创建的新数组。如果提供了 block,它应该实现 a 和 b 之间的比较(返回 -1、0 或 1)。
44array.sort! 或 array.sort! { |a, b| block }
就地排序 array。
45array.uniq
通过从 array 中移除重复值返回一个新数组。
46array.uniq!
就地从 array 中移除重复元素。如果未做任何更改,则返回 nil。
47array.unshift(obj, ...) 或 array.prepend(obj, ...)
将 obj(s) 前置到 array 的开头,并将其他元素向后移动。就地修改 array。
48array.values_at(selector, ...)
返回一个数组,其中包含 array 中对应于给定 selector(s)(整数索引或 range)的元素。
49array.zip(other_array1, ...)
将参数转换为数组,然后将 array 的元素与每个参数中对应的元素合并。它会生成一个数组的数组。

pack 方法是一个强大的工具,用于根据模板字符串将数组转换为二进制字符串序列。这通常用于创建特定二进制格式的数据,以便进行 File I/O 或网络通信。

指令描述
@移动到输出字符串中的绝对位置。
AASCII 字符串(用空格填充,计数是宽度)。
aASCII 字符串(用 null 填充,计数是宽度)。
B位字符串(每个字节内位以降序排列)。
b位字符串(每个字节内位以升序排列)。
C无符号 8 位整数(unsigned char)。
c有符号 8 位整数(char)。
D, d双精度浮点数,原生格式。
E双精度浮点数,小端字节序。
e单精度浮点数,小端字节序。
F, f单精度浮点数,原生格式。
G双精度浮点数,网络(大端)字节序。
g单精度浮点数,网络(大端)字节序。
H十六进制字符串(高半字节在前)。
h十六进制字符串(低半字节在前)。
I无符号整数(通常是 32 位)。后缀 _ 表示平台原生,< 表示小端,> 表示大端。
i有符号整数(通常是 32 位)。后缀 _ 表示平台原生,< 表示小端,> 表示大端。
L无符号长整数(通常基于平台/后缀是 32 位或 64 位)。后缀 _ 表示平台原生,< 表示小端,> 表示大端。
l有符号长整数(通常基于平台/后缀是 32 位或 64 位)。后缀 _ 表示平台原生,< 表示小端,> 表示大端。
MQuoted-printable 编码 (RFC 2045)。
mBase64 编码字符串。
N无符号 32 位整数,网络(大端)字节序。
n无符号 16 位整数,网络(大端)字节序。
P指向结构的指针(指定长度的固定长度字符串)。
p指向 null 终止字符串的指针。
Q无符号 64 位整数。后缀 < 表示小端,> 表示大端。
q有符号 64 位整数。后缀 < 表示小端,> 表示大端。
S无符号短整数(通常是 16 位)。后缀 _ 表示平台原生,< 表示小端,> 表示大端。
s有符号短整数(通常是 16 位)。后缀 _ 表示平台原生,< 表示小端,> 表示大端。
UUTF-8 字符。
uUU 编码字符串。
V无符号 32 位整数,小端字节序。
v无符号 16 位整数,小端字节序。
wBER 压缩整数。
X回退一个字节。
xnull 字节。
ZASCII 字符串(null 终止)。如果计数是 *,则消耗所有剩余项。

此示例演示了将各种数据类型打包成二进制字符串:

#!/usr/bin/ruby
letters = ["a", "b", "c"]
numbers = [65, 66, 67] # A, B, C 的 ASCII 值
packed_A = letters.pack("A3A3A3")
puts "Packed with A3A3A3: #{packed_A.inspect}" # => "\"a b c \""
packed_a = letters.pack("a3a3a3")
puts "Packed with a3a3a3: #{packed_a.inspect}" # => "\"a\x00\x00b\x00\x00c\x00\x00\""
packed_chars = numbers.pack("ccc")
puts "Packed with ccc: #{packed_chars.inspect}" # => "\"ABC\""
puts "Packed string itself: #{packed_chars}" # => "ABC"
packed_network_short = [256*1 + 2].pack("n") # 258 的大端短整数表示
puts "Packed 258 as network short: #{packed_network_short.inspect}" # => "\"\x01\x02\""

unpack 方法(String#unpack)是 Array#pack 的对应方法,用于将二进制字符串转换回数据数组。

实际应用:

数组在 Ruby 中是基础概念。它们用于存储项目列表,例如来自数据库的用户记录、文件中的行或算法中的一系列步骤。其丰富的 API 使它们适用于各种数据操作任务,从简单的队列和栈到复杂的数据处理管道。