Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Show all substring replacement options in Ruby [duplicate]

Tags:

ruby

I’m trying to show all possible options for substring replacements. For example, if I have this string:

my_string = “yellow dogs are cooler than brown cats”

and here are the different options that could be replaced:

substitutions = {“yellow” => “black”, “brown” => “grey”}

How would I get the following output?

["black dogs are cooler than brown cats", "black dogs are cooler than grey cats", "yellow dogs are cooler than brown cats", "yellow dogs are cooler than grey cats"]

So far I just have this, and as you can see, it’s not getting all options:

substitutions.each do |key, value|
    puts my_string.sub(key, value) if my_string.include? key
end
like image 990
user2270029 Avatar asked Mar 26 '26 06:03

user2270029


2 Answers

What you really need is to turn this

substitutions = {“yellow” => “black”, “brown” => “grey”}

into this:

[{"yellow"=>"yellow", "brown"=>"brown"},
 {"yellow"=>"yellow", "brown"=>"grey"},
 {"yellow"=>"black", "brown"=>"brown"},
 {"yellow"=>"black", "brown"=>"grey"}]

Then you can trivially loop over the above and get your desired output (you initially omitted the identity transformation, but it's most straightforward to include it). Here is one way to change substitutions into that:

substitutions.to_a[0].product(substitutions.to_a[1]).map do |ary|
  Hash[substitutions.keys.zip ary]
end 

Then it's straightforward:

_.each do |h|
  p my_string.split.map {|word| h.fetch(word,word) }.join(' ')
end
"yellow dogs are cooler than brown cats"
"yellow dogs are cooler than grey cats"
"black dogs are cooler than brown cats"
"black dogs are cooler than grey cats"

here I opted for split -> sub each word -> rejoin over a global sub operation, since you presumably want to replace "brown" but not "brownie". On the other hand this doesn't handle punctuation very gracefully, so you may opt for doing sub or some more sophisticated regex op. Up to you.

like image 93
roippi Avatar answered Mar 28 '26 01:03

roippi


Here's a generic solution that works for any number of substitutions (not just two):

my_string = "yellow dogs are cooler than brown cats"
substitutions = {"yellow"=>"black", "brown"=>"grey", "cooler"=>"sweeter"}
keys = substitutions.keys           #=> ["yellow","brown","cooler"]
regx = /\b#{Regexp.union(keys)}\b/  #=> /\b(?:yellow|brown|cooler)\b/
axes = substitutions.to_a           #=> [["yellow", "black"], ["brown", "grey"], ["cooler", "sweeter"]]
maps = axes.shift.product(*axes).map{ |*vals| Hash[ keys.zip(*vals) ] }
maps.each{ |map| p map, my_string.gsub(regx){ |match| map[match] } }

#=> {"yellow"=>"yellow", "brown"=>"brown", "cooler"=>"cooler"}
#=> "yellow dogs are cooler than brown cats"

#=> {"yellow"=>"yellow", "brown"=>"brown", "cooler"=>"sweeter"}
#=> "yellow dogs are sweeter than brown cats"

#=> {"yellow"=>"yellow", "brown"=>"grey", "cooler"=>"cooler"}
#=> "yellow dogs are cooler than grey cats"

#=> {"yellow"=>"yellow", "brown"=>"grey", "cooler"=>"sweeter"}
#=> "yellow dogs are sweeter than grey cats"

#=> {"yellow"=>"black", "brown"=>"brown", "cooler"=>"cooler"}
#=> "black dogs are cooler than brown cats"

#=> {"yellow"=>"black", "brown"=>"brown", "cooler"=>"sweeter"}
#=> "black dogs are sweeter than brown cats"

#=> {"yellow"=>"black", "brown"=>"grey", "cooler"=>"cooler"}
#=> "black dogs are cooler than grey cats"

#=> {"yellow"=>"black", "brown"=>"grey", "cooler"=>"sweeter"}
#=> "black dogs are sweeter than grey cats"

The key here is the product call, which turns [[1,2],[3,4],[5,6]] into:

[[1, 3, 5],
 [1, 3, 6],
 [1, 4, 5],
 [1, 4, 6],
 [2, 3, 5],
 [2, 3, 6],
 [2, 4, 5],
 [2, 4, 6]]

In other words, all the combinations from the calling array and all arrays passed in.

Using the regular expression with gsub makes it efficient, doing one pass through the string for each full set of replacements. The block form of gsub yields the found text, which is then used to look up the desired replacement (sometimes the same string as the original).

like image 42
Phrogz Avatar answered Mar 27 '26 23:03

Phrogz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!