Ruby 数组方法:each、map、select 的使用
Ruby 的数组是日常开发中最常用的数据结构之一。熟练掌握数组的迭代方法,能让你的代码更简洁、更易读。本文将详细介绍 each、map、select 这三个最实用的数组方法,帮你快速提升编码效率。
先理解一个核心概念:块(Block)
在学习这些方法之前,需要先理解 Ruby 的块(Block)特性。块是一段可以传递给方法的代码,用 {} 或 do...end 包裹。
# 两种写法等效
[1, 2, 3].each { |num| puts num }
[1, 2, 3].each do |num|
puts num
end
管道符号 | 之间的变量是块的参数,每次迭代时会接收数组中的一个元素。
each 方法:逐个处理
each 是最基础的迭代方法,用于遍历数组中的每个元素。它不会改变原数组,也不会返回新数组——它的返回值是原始数组本身。
基本用法
numbers = [1, 2, 3, 4, 5]
result = numbers.each do |num|
puts "当前数字: #{num}"
end
puts result.inspect # => [1, 2, 3, 4, 5]
执行后,你会看到输出依次是 当前数字: 1、当前数字: 2……直到 当前数字: 5。虽然我们在块里做了操作,但 result 变量最终得到的仍是原始数组。
常见应用场景
each 适合用于执行副作用操作,比如打印日志、更新界面、写入文件等不关心返回值的场景。
users = ["Alice", "Bob", "Charlie"]
users.each { |user| puts "处理用户: #{user}" }
# 输出:
# 处理用户: Alice
# 处理用户: Bob
# 处理用户: Charlie
map 方法:转换数据
map 方法会对每个元素执行块中的操作,并将结果收集起来返回一个新数组。这是数据转换的利器。
基本用法
numbers = [1, 2, 3, 4, 5]
squares = numbers.map do |num|
num * num
end
puts squares.inspect # => [1, 4, 9, 16, 25]
numbers 本身没有任何变化,但 squares 是一个全新的数组,包含了每个元素的平方值。
实战示例
names = ["alice", "bob", "charlie"]
capitalized = names.map { |name| name.capitalize }
puts capitalized.inspect # => ["Alice", "Bob", "Charlie"]
# 链式调用
ids = [101, 102, 103]
formatted = ids.map { |id| "USER-#{id}" }
puts formatted.inspect # => ["USER-101", "USER-102", "USER-103"]
与 each 的关键区别
numbers = [1, 2, 3]
# each 返回原数组
each_result = numbers.each { |n| n * 2 }
puts each_result.inspect # => [1, 2, 3]
# map 返回新数组
map_result = numbers.map { |n| n * 2 }
puts map_result.inspect # => [2, 4, 6]
这个区别至关重要。each 的返回值总是被忽略的原始数组,而 map 的返回值才是我们真正想要的新数组。
select 方法:筛选元素
select 方法会根据块中的条件判断来筛选元素,返回一个只包含满足条件元素的新数组。也称为过滤操作。
基本用法
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = numbers.select do |num|
num.even?
end
puts evens.inspect # => [2, 4, 6, 8, 10]
只有偶数被保留了下来。块的返回值必须是布尔值(true 或 false),返回 true 的元素会被选中。
实战示例
products = [
{ name: "笔记本", price: 50 },
{ name: "手机", price: 3000 },
{ name: "耳机", price: 200 },
{ name: "平板", price: 2500 }
]
# 筛选价格超过 1000 的商品
expensive = products.select { |p| p[:price] > 1000 }
puts expensive.inspect
# => [{:name=>"手机", :price=>3000}, {:name=>"平板", :price=>2500}]
# 筛选名称包含"机"的产品
keyword = products.select { |p| p[:name].include?("机") }
puts keyword.inspect
# => [{:name=>"手机", :price=>3000}, {:name=>"平板", :price=>2500}]
与 map 的对比
| 场景 | 方法 | 作用 |
|---|---|---|
| 转换每个元素 | map |
[1,2,3].map { \|n\| n*2 } → [2,4,6] |
| 筛选满足条件的元素 | select |
[1,2,3].select { \|n\| n>1 } → [2,3] |
三者对比总结
data = [10, 20, 30, 40, 50]
# each:遍历执行,不关心返回值
data.each { |n| puts n * 2 }
# 输出: 20, 40, 60, 80, 100
# 返回: [10, 20, 30, 40, 50]
# map:转换每个元素,返回新数组
mapped = data.map { |n| n * 2 }
# 返回: [20, 40, 60, 80, 100]
# select:筛选满足条件的元素,返回新数组
filtered = data.select { |n| n > 25 }
# 返回: [30, 40, 50]
记忆口诀
- each → 逐个过一遍,不返回值
- map → 映射成新值,生成新数组
- select → 挑选合格的,留下筛选后的结果
组合使用:威力翻倍
这三个方法可以链式调用,处理复杂的业务逻辑。
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 筛选偶数 -> 平方 -> 求和
result = numbers
.select { |n| n.even? } # [2, 4, 6, 8, 10]
.map { |n| n * n } # [4, 16, 36, 64, 100]
.sum # 220
puts result # => 220
users = [
{ name: "张三", age: 25, active: true },
{ name: "李四", age: 30, active: false },
{ name: "王五", age: 28, active: true },
{ name: "赵六", age: 35, active: true }
]
# 获取所有活跃用户的名字
active_names = users
.select { |u| u[:active] }
.map { |u| u[:name] }
puts active_names.inspect # => ["张三", "王五", "赵六"]
常见错误与注意事项
1. 混淆 each 和 map 的返回值
# 错误写法:想获取平方值却用了 each
numbers = [1, 2, 3]
squares = numbers.each { |n| n * n }
puts squares.inspect # => [1, 2, 3] 不是想要的 [1, 4, 9]
# 正确写法
squares = numbers.map { |n| n * n }
puts squares.inspect # => [1, 4, 9]
2. 在 select 中忘记返回布尔值
# 错误:返回了数值,而不是布尔值
[1, 2, 3, 4].select { |n| n * 2 }
# Ruby 会将非 nil 和非 false 都视为真,所以会返回整个数组
# 正确:明确返回判断结果
[1, 2, 3, 4].select { |n| n > 2 } # => [3, 4]
3. 修改原数组
这三个方法都不会修改原数组。如果你想原地修改,可以使用 map!、select! 等带感叹号的版本。
numbers = [1, 2, 3, 4, 5]
numbers.map! { |n| n * 2 }
puts numbers.inspect # => [2, 4, 6, 8, 10]
选择指南
| 需求 | 推荐方法 |
|---|---|
| 只是遍历并执行操作(如打印、写入) | each |
| 需要对每个元素进行转换,生成新数组 | map |
| 需要根据条件筛选出部分元素 | select |
| 需要转换后继续筛选或再次转换 | 链式调用 |
掌握这三种方法后,你会发现Ruby代码变得简洁而强大。勤加练习,让它们成为你日常编码的得力工具。

暂无评论,快来抢沙发吧!