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

Ruby:如何通过谓词单次迭代拆分集合为两个集合?Hash是否支持?

集合按谓词单次迭代拆分的可读实现及Hash支持情况

需要将单个集合按指定谓词拆分为两个集合——一个包含符合谓词的元素,另一个包含不符合的元素,要求仅进行一次迭代且避免不必要的临时拷贝。目前已有如下可行实现:

irb> aaa = [ 1, 2, 3, 4 ,5, 6, 7, 8, 9 ]; bbb = [];
irb> aaa.reject!{|n| bbb<<n unless n.odd? }
irb> bbb
# => [2, 4, 6, 8]
irb> aaa
# => [1, 3, 5, 7, 9]

但该实现可读性不足,同时希望了解Hash是否支持此类操作。


更可读的数组拆分方案

Ruby的Enumerable#partition方法正是为这类场景设计的,它会单次迭代集合,将元素按谓词条件拆分为两个数组返回,代码简洁易懂,完全符合单次迭代的要求:

aaa = [1, 2, 3, 4, 5, 6, 7, 8, 9]
yes_set, no_set = aaa.partition(&:odd?)

yes_set # => [1, 3, 5, 7, 9](符合谓词的元素)
no_set  # => [2, 4, 6, 8](不符合谓词的元素)

如果需要原地修改原数组(像原实现那样将符合条件的元素留在原数组,不符合的移到新数组),可以用更清晰的写法替代原reject!逻辑:

aaa = [1, 2, 3, 4, 5, 6, 7, 8, 9]
no_set = aaa.select!(&:odd?).then { aaa - _1 }

若严格要求无临时拷贝,原实现的逻辑本质上是最优的,但可以通过提取方法来大幅提升可读性:

def split_in_place(collection, predicate)
  rejected = []
  collection.reject! { |item| rejected << item unless predicate.call(item) }
  [collection, rejected]
end

aaa = [1, 2, 3, 4, 5, 6, 7, 8, 9]
yes_set, no_set = split_in_place(aaa, ->(n) { n.odd? })

Hash的拆分实现

Hash同样属于Enumerable,因此可以直接使用partition方法,之后将拆分出的数组转换为Hash即可:

my_hash = { a: 1, b: 2, c: 3, d: 4 }
yes_hash, no_hash = my_hash.partition { |_key, value| value.odd? }.map(&:to_h)

yes_hash # => {:a=>1, :c=>3}
no_hash  # => {:b=>2, :d=>4}

也可以用group_by方法按谓词分组,再分别转换为Hash,同样是单次迭代完成:

my_hash = { a: 1, b: 2, c: 3, d: 4 }
grouped = my_hash.group_by { |_key, value| value.odd? }

yes_hash = grouped[true].to_h
no_hash = grouped[false].to_h

内容的提问来源于stack exchange,提问作者Toni Schilling

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 10:51:11