【Ruby】文字列内の複数パターン置換をHashで行う

Ruby3.0 , 2.7

Rubyでの文字列の置換手段には、str.gsub(pattern, replace) があります。
gsub() は、pattern でヒットした文字列を replace に置き換える関数です。
公式ドキュメントに記載がないんですが、一行で一つのパターンじゃなくて、複数パターンに対応して辞書的な置換がしたいです。

[Ruby 複数 置換] とかで調べると、こんなのばっかりでてきます。

origin_text = "abcdef"
origin_text.gsub(/a|bc|def/, "a" => "apple", "bc" => "back", "def" => "define")
=>"applebackdefine"

バカがよ、絶対こんなことしたくないだろ。ひとつひとつ真心こめて膨大な数の指定しろっていうのか?
ハッシュ(辞書)とか変数を使って動的に指定させろ。
……と思いました。(コピペなんとかサムライアカデミーのような量産カスサイトを揶揄するもので、有志で情報を共有してくれているエンジニアを貶める意図はありません。)

gsub() では、正規表現で置換対象を指定します。
なので、 Ruby の正規表現でハッシュを使うにはどうしたらいいのかを調べました。
どうも、第二引数の replace にはハッシュが指定できるようです。
つまり、第一引数の pattern をどうにかすればいいようでした。

結論です。

origin_text = "abcdef"
gsubdict = {"a"=>'apple', "bc"=>"back", "def"=>"define"}
replace_words = Regexp.union(gsubdict.keys) 
origin_text.gsub(replace_words, gsubdict)
=>"applebackdefine"

正規表現パターンを動的に生成しています。
'/a|bc|def/' という '/' を含んだ文字列(※)を作ればよいです。
しかし、単純にそのままテキストに落とし込んだ場合は '/' を正規表現パターンの開始文字として認識してくれません。
なので、Regexp.union() を使って「これは正規表現パターンですよ」というRegexp Classにします。引数に配列を与えるだけでよろしくやってくれます。
hash.keys で ハッシュのキーだけ配列で取り出して Regexp.union() に与えると正規表現パターンが返ってきます。
これを gsub() の第一引数に与えて、辞書を第二引数に与えて目的達成です。

(※)……便宜的表現

コメント