Ruby 数组(Array)
数组是Ruby中最常用的数据结构之一,用于存储有序的元素集合。Ruby数组功能强大且灵活,可以存储不同类型的元素,并提供了丰富的内置方法来操作数据。本章将详细介绍Ruby中数组的创建、操作和处理方法。
🎯 数组基础
数组定义
Ruby提供了多种方式来创建数组:
ruby
# 使用字面量语法
fruits = ["苹果", "香蕉", "橙子"]
numbers = [1, 2, 3, 4, 5]
mixed = ["字符串", 123, true, nil]
# 使用Array.new方法
empty_array = Array.new
sized_array = Array.new(3) # [nil, nil, nil]
initialized_array = Array.new(3, "默认值") # ["默认值", "默认值", "默认值"]
# 使用%w快捷方式创建字符串数组
words = %w[hello world ruby programming]
# 等同于 ["hello", "world", "ruby", "programming"]
# 使用%i快捷方式创建符号数组
symbols = %i[apple banana orange]
# 等同于 [:apple, :banana, :orange]
# 使用范围创建数组
range_array = (1..5).to_a # [1, 2, 3, 4, 5]
range_array2 = ("a".."e").to_a # ["a", "b", "c", "d", "e"]
# 从其他对象创建数组
array_from_string = "hello".split("") # ["h", "e", "l", "l", "o"]
array_from_range = Array(1..3) # [1, 2, 3]数组访问
ruby
fruits = ["苹果", "香蕉", "橙子", "葡萄", "草莓"]
# 通过索引访问元素
puts fruits[0] # 苹果
puts fruits[2] # 橙子
puts fruits[-1] # 草莓 (负索引从末尾开始)
puts fruits[-2] # 葡萄
# 通过范围访问多个元素
puts fruits[1..3].inspect # ["香蕉", "橙子", "葡萄"]
puts fruits[1...3].inspect # ["香蕉", "橙子"] (不包含结束索引)
puts fruits[-3..-1].inspect # ["橙子", "葡萄", "草莓"]
# 使用at方法访问
puts fruits.at(1) # 香蕉
# 获取第一个和最后一个元素
puts fruits.first # 苹果
puts fruits.last # 草莓
# 获取前几个或后几个元素
puts fruits.first(3).inspect # ["苹果", "香蕉", "橙子"]
puts fruits.last(2).inspect # ["葡萄", "草莓"]
# 检查数组是否包含某个元素
puts fruits.include?("香蕉") # true
puts fruits.include?("西瓜") # false📏 数组属性
数组长度和大小
ruby
numbers = [1, 2, 3, 4, 5]
# 获取数组长度
puts numbers.length # 5
puts numbers.size # 5 (length的别名)
# 检查数组是否为空
puts numbers.empty? # false
puts [].empty? # true
# 获取数组的容量信息
puts numbers.count # 5
puts numbers.count(3) # 1 (统计特定元素的数量)
puts numbers.count { |n| n > 3 } # 2 (满足条件的元素数量)数组比较
ruby
arr1 = [1, 2, 3]
arr2 = [1, 2, 3]
arr3 = [3, 2, 1]
# 相等性比较
puts arr1 == arr2 # true
puts arr1 == arr3 # false
# 比较数组内容(考虑顺序)
puts arr1.eql?(arr2) # true
# 对象标识比较
puts arr1.equal?(arr2) # false (不同的对象)
puts arr1.equal?(arr1) # true (同一个对象)
# 数组比较(按字典序)
puts [1, 2, 3] <=> [1, 2, 3] # 0 (相等)
puts [1, 2, 3] <=> [1, 2, 4] # -1 (小于)
puts [1, 2, 4] <=> [1, 2, 3] # 1 (大于)➕ 数组操作
添加元素
ruby
# 使用push或<<在末尾添加元素
fruits = ["苹果", "香蕉"]
fruits.push("橙子")
fruits << "葡萄" # 简写形式
puts fruits.inspect # ["苹果", "香蕉", "橙子", "葡萄"]
# 使用unshift在开头添加元素
fruits.unshift("草莓")
puts fruits.inspect # ["草莓", "苹果", "香蕉", "橙子", "葡萄"]
# 在指定位置插入元素
fruits.insert(2, "芒果", "梨")
puts fruits.inspect # ["草莓", "苹果", "芒果", "梨", "香蕉", "橙子", "葡萄"]
# 使用concat连接数组
more_fruits = ["樱桃", "桃子"]
fruits.concat(more_fruits)
puts fruits.inspect # ["草莓", "苹果", "芒果", "梨", "香蕉", "橙子", "葡萄", "樱桃", "桃子"]删除元素
ruby
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 删除最后一个元素
last_element = numbers.pop
puts last_element # 10
puts numbers.inspect # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 删除第一个元素
first_element = numbers.shift
puts first_element # 1
puts numbers.inspect # [2, 3, 4, 5, 6, 7, 8, 9]
# 删除指定值的元素
numbers.delete(5)
puts numbers.inspect # [2, 3, 4, 6, 7, 8, 9]
# 删除指定索引的元素
deleted_element = numbers.delete_at(0)
puts deleted_element # 2
puts numbers.inspect # [3, 4, 6, 7, 8, 9]
# 删除满足条件的元素
numbers.delete_if { |n| n > 7 }
puts numbers.inspect # [3, 4, 6, 7]
# 清空数组
numbers.clear
puts numbers.inspect # []修改元素
ruby
letters = ["a", "b", "c", "d", "e"]
# 通过索引修改单个元素
letters[0] = "A"
puts letters.inspect # ["A", "b", "c", "d", "e"]
# 通过范围修改多个元素
letters[1..2] = ["B", "C"]
puts letters.inspect # ["A", "B", "C", "d", "e"]
# 使用fill填充元素
numbers = [1, 2, 3, 4, 5]
numbers.fill(0, 2, 2) # 从索引2开始,填充2个元素为0
puts numbers.inspect # [1, 2, 0, 0, 5]
# 使用fill填充整个数组
empty_array = [nil, nil, nil]
empty_array.fill("默认值")
puts empty_array.inspect # ["默认值", "默认值", "默认值"]🔍 数组查找和筛选
查找元素
ruby
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 查找第一个满足条件的元素
even_number = numbers.find { |n| n.even? }
puts even_number # 2
# 查找所有满足条件的元素
even_numbers = numbers.select { |n| n.even? }
puts even_numbers.inspect # [2, 4, 6, 8, 10]
# 查找不满足条件的元素
odd_numbers = numbers.reject { |n| n.even? }
puts odd_numbers.inspect # [1, 3, 5, 7, 9]
# 查找元素的索引
index = numbers.index(5)
puts index # 4
# 查找满足条件的元素索引
index = numbers.index { |n| n > 5 }
puts index # 5 (第一个大于5的元素索引)数组筛选和分组
ruby
# 使用take和drop
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
puts numbers.take(3).inspect # [1, 2, 3] (取前3个)
puts numbers.drop(3).inspect # [4, 5, 6, 7, 8, 9, 10] (跳过前3个)
# 使用take_while和drop_while
puts numbers.take_while { |n| n < 5 }.inspect # [1, 2, 3, 4]
puts numbers.drop_while { |n| n < 5 }.inspect # [5, 6, 7, 8, 9, 10]
# 分组
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
grouped = numbers.group_by { |n| n.even? ? "偶数" : "奇数" }
puts grouped
# {"奇数"=>[1, 3, 5, 7, 9], "偶数"=>[2, 4, 6, 8, 10]}
# 分块
data = [1, 2, 3, 4, 5, 6, 7, 8]
chunks = data.each_slice(3).to_a
puts chunks.inspect # [[1, 2, 3], [4, 5, 6], [7, 8]]
# 分区
partitioned = numbers.partition { |n| n.even? }
puts partitioned.inspect # [[2, 4, 6, 8, 10], [1, 3, 5, 7, 9]]🔧 数组变换
映射和转换
ruby
numbers = [1, 2, 3, 4, 5]
# 使用map转换每个元素
squared = numbers.map { |n| n * n }
puts squared.inspect # [1, 4, 9, 16, 25]
# 使用collect(map的别名)
doubled = numbers.collect { |n| n * 2 }
puts doubled.inspect # [2, 4, 6, 8, 10]
# 就地修改(使用map!)
numbers.map! { |n| n * 2 }
puts numbers.inspect # [2, 4, 6, 8, 10]
# 扁平化嵌套数组
nested = [[1, 2], [3, 4], [5, 6]]
flattened = nested.flatten
puts flattened.inspect # [1, 2, 3, 4, 5, 6]
# 深度扁平化
deep_nested = [1, [2, [3, 4]], 5]
deep_flattened = deep_nested.flatten(1) # 只扁平化一层
puts deep_flattened.inspect # [1, 2, [3, 4], 5]排序和反转
ruby
# 数字排序
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
sorted_asc = numbers.sort
puts sorted_asc.inspect # [1, 1, 2, 3, 4, 5, 6, 9]
sorted_desc = numbers.sort { |a, b| b <=> a }
puts sorted_desc.inspect # [9, 6, 5, 4, 3, 2, 1, 1]
# 使用sort_by按特定条件排序
words = ["apple", "banana", "cherry", "date"]
sorted_by_length = words.sort_by { |word| word.length }
puts sorted_by_length.inspect # ["date", "apple", "cherry", "banana"]
# 反转数组
reversed = numbers.reverse
puts reversed.inspect # [6, 2, 9, 5, 1, 4, 1, 3]
# 就地反转
numbers.reverse!
puts numbers.inspect # [6, 2, 9, 5, 1, 4, 1, 3]
# 去除重复元素
duplicates = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
unique = duplicates.uniq
puts unique.inspect # [1, 2, 3, 4]
# 按条件去重
people = [
{name: "张三", age: 25},
{name: "李四", age: 30},
{name: "张三", age: 28}
]
unique_by_name = people.uniq { |person| person[:name] }
puts unique_by_name.inspect
# [{:name=>"张三", :age=>25}, {:name=>"李四", :age=>30}]🔁 数组迭代
基本迭代
ruby
fruits = ["苹果", "香蕉", "橙子"]
# 使用each遍历
fruits.each { |fruit| puts "我喜欢吃#{fruit}" }
# 我喜欢吃苹果
# 我喜欢吃香蕉
# 我喜欢吃橙子
# 使用each_with_index获取索引
fruits.each_with_index do |fruit, index|
puts "#{index + 1}. #{fruit}"
end
# 1. 苹果
# 2. 香蕉
# 3. 橙子
# 使用reverse_each反向遍历
fruits.reverse_each { |fruit| puts fruit }
# 橙子
# 香蕉
# 苹果高级迭代
ruby
numbers = [1, 2, 3, 4, 5]
# 使用map进行转换
squared = numbers.map { |n| n ** 2 }
puts squared.inspect # [1, 4, 9, 16, 25]
# 使用each_with_object累积结果
sum = numbers.each_with_object(0) { |n, total| total += n }
puts sum # 15
# 使用reduce/inject进行累积
sum = numbers.reduce(0) { |total, n| total + n }
puts sum # 15
product = numbers.reduce(1) { |total, n| total * n }
puts product # 120
# 使用reduce简化写法
sum = numbers.reduce(:+)
product = numbers.reduce(:*)
# 使用zip合并数组
letters = ["a", "b", "c"]
numbers = [1, 2, 3]
combined = letters.zip(numbers)
puts combined.inspect # [["a", 1], ["b", 2], ["c", 3]]
# 使用transpose转置数组
matrix = [[1, 2, 3], [4, 5, 6]]
transposed = matrix.transpose
puts transposed.inspect # [[1, 4], [2, 5], [3, 6]]🎯 数组实践示例
数据处理管道
ruby
class DataProcessor
def initialize(data)
@data = data
end
# 链式方法调用
def filter(&block)
DataProcessor.new(@data.select(&block))
end
def map(&block)
DataProcessor.new(@data.map(&block))
end
def sort(&block)
DataProcessor.new(@data.sort(&block))
end
def take(n)
DataProcessor.new(@data.take(n))
end
def result
@data
end
end
# 使用数据处理管道
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = DataProcessor.new(numbers)
.filter { |n| n.even? }
.map { |n| n ** 2 }
.sort { |a, b| b <=> a }
.take(3)
.result
puts result.inspect # [100, 64, 36]数组统计分析
ruby
class ArrayStatistics
def initialize(array)
@array = array
end
# 计算平均值
def mean
return 0 if @array.empty?
@array.sum.to_f / @array.length
end
# 计算中位数
def median
return nil if @array.empty?
sorted = @array.sort
length = sorted.length
if length.odd?
sorted[length / 2]
else
(sorted[length / 2 - 1] + sorted[length / 2]) / 2.0
end
end
# 计算众数
def mode
return nil if @array.empty?
frequency = Hash.new(0)
@array.each { |n| frequency[n] += 1 }
max_frequency = frequency.values.max
frequency.select { |k, v| v == max_frequency }.keys
end
# 计算方差
def variance
return 0 if @array.empty?
avg = mean
sum = @array.reduce(0) { |total, n| total + (n - avg) ** 2 }
sum / @array.length
end
# 计算标准差
def standard_deviation
Math.sqrt(variance)
end
# 获取统计摘要
def summary
{
count: @array.length,
mean: mean,
median: median,
mode: mode,
min: @array.min,
max: @array.max,
range: @array.max - @array.min,
variance: variance,
standard_deviation: standard_deviation
}
end
end
# 使用统计分析
data = [1, 2, 2, 3, 4, 5, 5, 5, 6, 7, 8, 9]
stats = ArrayStatistics.new(data)
puts stats.summary
# {:count=>12, :mean=>4.75, :median=>5.0, :mode=>[5], :min=>1, :max=>9, :range=>8, :variance=>5.6875, :standard_deviation=>2.384842971765449}数组分页器
ruby
class ArrayPaginator
def initialize(array, per_page = 10)
@array = array
@per_page = per_page
end
# 获取总页数
def total_pages
(@array.length.to_f / @per_page).ceil
end
# 获取指定页的数据
def page(page_number)
return [] if page_number < 1 || page_number > total_pages
start_index = (page_number - 1) * @per_page
end_index = start_index + @per_page - 1
@array[start_index..end_index]
end
# 检查是否有下一页
def has_next_page?(current_page)
current_page < total_pages
end
# 检查是否有上一页
def has_prev_page?(current_page)
current_page > 1
end
# 获取下一页
def next_page(current_page)
current_page + 1 if has_next_page?(current_page)
end
# 获取上一页
def prev_page(current_page)
current_page - 1 if has_prev_page?(current_page)
end
end
# 使用分页器
data = (1..100).to_a
paginator = ArrayPaginator.new(data, 10)
puts "总页数: #{paginator.total_pages}" # 总页数: 10
puts "第1页: #{paginator.page(1).inspect}" # 第1页: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
puts "第5页: #{paginator.page(5).inspect}" # 第5页: [41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
puts "是否有下一页(5): #{paginator.has_next_page?(5)}" # 是否有下一页(5): true
puts "下一页(5): #{paginator.next_page(5)}" # 下一页(5): 6🔄 数组与其它数据结构
数组与哈希转换
ruby
# 数组转换为哈希
pairs = [["name", "张三"], ["age", 25], ["city", "北京"]]
person_hash = pairs.to_h
puts person_hash.inspect # {"name"=>"张三", "age"=>25, "city"=>"北京"}
# 使用关联数组创建哈希
fruits = [:apple, :banana, :orange]
prices = [3.5, 2.0, 4.0]
fruit_prices = fruits.zip(prices).to_h
puts fruit_prices.inspect # {:apple=>3.5, :banana=>2.0, :orange=>4.0}
# 哈希转换为数组
person = {name: "张三", age: 25, city: "北京"}
pairs = person.to_a
puts pairs.inspect # [[:name, "张三"], [:age, 25], [:city, "北京"]]
keys = person.keys
puts keys.inspect # [:name, :age, :city]
values = person.values
puts values.inspect # ["张三", 25, "北京"]数组与范围
ruby
# 范围转换为数组
numbers = (1..10).to_a
puts numbers.inspect # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 使用步长
even_numbers = (2..20).step(2).to_a
puts even_numbers.inspect # [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
# 字符范围
letters = ("a".."z").to_a
puts letters.take(5).inspect # ["a", "b", "c", "d", "e"]
# 数组转换为范围(如果可能)
def array_to_range(arr)
return nil if arr.empty?
min, max = arr.minmax
(min..max) if (min..max).to_a == arr.sort
end
consecutive = [1, 2, 3, 4, 5]
puts array_to_range(consecutive) # 1..5
non_consecutive = [1, 3, 5, 7]
puts array_to_range(non_consecutive) # nil📊 数组性能优化
大数组处理
ruby
# 使用lazy枚举处理大数组
large_array = (1..1_000_000).to_a
# 普通方式(会处理所有元素)
# result = large_array.select { |n| n.even? }.map { |n| n ** 2 }.first(10)
# 使用lazy延迟计算
result = large_array.lazy
.select { |n| n.even? }
.map { |n| n ** 2 }
.first(10)
puts result.inspect # [4, 16, 36, 64, 100, 144, 196, 256, 324, 400]
# 使用each_slice处理大数据集
def process_large_array(array, batch_size = 1000)
array.each_slice(batch_size) do |batch|
# 处理每个批次
puts "处理批次,大小: #{batch.length}"
# 实际处理逻辑...
end
end
# 内存友好的迭代
def memory_efficient_iteration(array)
array.each do |item|
# 处理单个项目
# 避免创建大的中间数组
end
end数组操作优化
ruby
# 高效的数组构建
# 避免重复的数组连接
# 低效:
# result = []
# (1..1000).each { |i| result = result + [i] }
# 高效:
result = []
(1..1000).each { |i| result << i }
# 或者直接使用map
result = (1..1000).map { |i| i }
# 预分配数组大小
def preallocated_array(size)
array = Array.new(size)
(0...size).each { |i| array[i] = i * 2 }
array
end
# 使用数组作为栈(推荐使用push/pop)
stack = []
stack.push(1) # 入栈
stack.push(2)
element = stack.pop # 出栈
# 使用数组作为队列(推荐使用push/shift)
queue = []
queue.push(1) # 入队
queue.push(2)
element = queue.shift # 出队🎯 数组最佳实践
1. 选择合适的方法
ruby
# 对于简单遍历,使用each
[1, 2, 3].each { |n| puts n }
# 对于转换,使用map
squared = [1, 2, 3].map { |n| n ** 2 }
# 对于筛选,使用select
evens = [1, 2, 3, 4, 5].select(&:even?)
# 对于检查条件,使用any?/all?
has_even = [1, 2, 3].any?(&:even?) # true
all_positive = [1, 2, 3].all? { |n| n > 0 } # true
# 对于累积操作,使用reduce
sum = [1, 2, 3, 4, 5].reduce(:+)2. 安全处理数组
ruby
class SafeArrayHandler
# 安全访问数组元素
def self.safe_access(array, index)
return nil if array.nil? || !array.is_a?(Array)
return nil if index < 0 || index >= array.length
array[index]
end
# 安全获取数组片段
def self.safe_slice(array, start_index, length)
return [] if array.nil? || !array.is_a?(Array)
array.slice(start_index, length) || []
end
# 安全的数组连接
def self.safe_concat(array1, array2)
return array1 || [] if array2.nil?
return array2 || [] if array1.nil?
(array1 || []) + (array2 || [])
end
end
# 使用安全处理
arr = [1, 2, 3]
puts SafeArrayHandler.safe_access(arr, 1) # 2
puts SafeArrayHandler.safe_access(arr, 10) # nil
puts SafeArrayHandler.safe_slice(arr, 1, 2) # [2, 3]3. 数组验证
ruby
class ArrayValidator
# 验证是否为数组
def self.array?(obj)
obj.is_a?(Array)
end
# 验证数组大小
def self.size_between?(array, min, max)
return false unless array?(array)
array.length.between?(min, max)
end
# 验证数组元素类型
def self.all_of_type?(array, type)
return false unless array?(array)
array.all? { |element| element.is_a?(type) }
end
# 验证数组不为空且不包含nil
def self.present_and_no_nil?(array)
return false unless array?(array)
!array.empty? && !array.include?(nil)
end
end
# 使用验证器
puts ArrayValidator.array?([1, 2, 3]) # true
puts ArrayValidator.array?("not array") # false
puts ArrayValidator.size_between?([1, 2, 3], 1, 5) # true
puts ArrayValidator.all_of_type?([1, 2, 3], Integer) # true
puts ArrayValidator.present_and_no_nil?([1, 2, 3]) # true📚 下一步学习
掌握了Ruby数组操作后,建议继续学习:
- Ruby 哈希(Hash) - 学习键值对数据结构
- Ruby 范围(Range) - 掌握范围对象的使用
- Ruby 迭代器 - 深入学习迭代模式
- Ruby 块及模块(Module) - 了解代码块和模块化编程
继续您的Ruby学习之旅吧!