You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby单行块与多行块行为差异:ActiveRecord查询输出不同原因

问题:两种代码实现为何输出不同?

预期输出应为ActiveRecord记录数组,但单行块实现会将过滤后的原数组嵌套追加到变量x中,多行块则能正常将查询得到的ActiveRecord记录追加到x中。

代码示例1(单行块)

x = []
y = ["", "1353", "1155"]
x << y.reject!(&:empty?).each { |i| User.find(i) }
# => ["1353", "1155"]
x
#=> [["1353", "1155"]]

代码示例2(多行块)

x = []
y = ["", "1353", "1155"]
y.reject!(&:empty?)
y.each do |i|
  x << User.find(i)  
end  
# => ["1353", "1155"]
x
#=> [#<User:0x00007fc7fbacc928
#  id: 1353,
#  login: nil,
...
原因解析

核心差异在于Ruby中each方法的返回值,以及单行代码的执行逻辑:

  • 单行块实现:y.reject!(&:empty?).each { |i| User.find(i) } 里,each会遍历数组并执行User.find(i)(这一步确实会触发ActiveRecord查询),但each的返回值始终是调用它的原数组,不是块内代码的执行结果。所以x << 实际上是把过滤后的y数组整个追加进去,导致x变成嵌套数组。
  • 多行块实现:先通过y.reject!(&:empty?)过滤数组,再在each块内部,每次将User.find(i)的结果单独追加到x中,最终x自然是ActiveRecord对象的数组。
优化写法

如果想用单行代码达到多行块的效果,可以改用map方法(map会返回块执行结果组成的新数组),再合并到x中:

x = []
y = ["", "1353", "1155"]
x += y.reject(&:empty?).map { |i| User.find(i) }
# 或用concat,效果一致
x.concat(y.reject(&:empty?).map { |i| User.find(i) })

这种写法既简洁,又不会修改原数组y(用reject而非reject!),最终x会直接得到预期的ActiveRecord记录数组。

内容的提问来源于stack exchange,提问作者Jacob Miller

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 16:00:15