一行中有多个就地替换

时间:2014-03-26 20:00:15

标签: ruby

当没有一个被替换的模式保证匹配时,在字符串上进行多个就地替换的最惯用的方法是什么?

例如,假设我有一个字符串数组,我想在每个字符串中将“sad”替换为“happy”,将“goodbye”替换为“hello”:

a = ["I am sad", "goodbye for now"]

# This will work:
a.map! do |s|
    s = s.gsub(/sad/,"happy").gsub(/goodbye/,"hello")
end
# So will this:
a.each do |s|
    s.gsub!(/sad/,"happy")
    s.gsub!(/goodbye/,"hello")
end
# This will fail when s does not match /sad/:
a.each do |s|
    s.gsub!(/sad/,"happy").gsub!(/goodbye/,"hello")
end

第一个选项似乎有点傻,因为逻辑上我正在尝试进行就地替换而不是重新分配。第二种选择是可以的,但我的审美意识告诉我,要求将替换变成两个连续的陈述似乎是错误的,特别是在预期只有一个或另一个替换成功的情况下(具有讽刺意味的是,确切地说)导致第三个版本(看起来对我来说)失败的情况。此外,我在这里使用each破坏性可能是错误的,但如果我使用map!代替,我需要添加s作为块中的最后一行确保在替换失败时我不会意外地发出nil个条目,这似乎比第一个选项差一些。

我猜测(g)sub!方法在没有进行替换时返回nil的原因是因为这使得它们便于在逻辑结构中使用,这无疑是一个很好的理由(特别是因为非破坏性版本显然必须返回“真实”值,无论如何)。

所以...我知道这实际上只是一个小小的美学狡辩,但是有没有比我展示的两种(工作)方式更好的方法?如果没有,是否有理由偏爱另一个(超出我对第二个版本的直观审美偏好)?

1 个答案:

答案 0 :(得分:7)

首先,您需要创建一个replacement_hash,如下所示:

replacement_hash = { "sad" => "happy", "goodbye" => "hello"}
a = ["I am sad", "goodbye for now"]
Regexp.union(replacement_hash.keys) # => /sad|goodbye/
a.map { |s| s.gsub(Regexp.union(replacement_hash.keys), replacement_hash) }
# => ["I am happy", "hello for now"]

如果需要就地替换,请执行以下操作: -

a.each { |s| s.gsub!(Regexp.union(replacement_hash.keys), replacement_hash) }