I was wondering if there is a simple way to do every combination of selected character substitutions in ruby in a simple way.
An example:
string
Another way:
string = "this is a test"
subs = [{"a"=>"@"}, {"i"=>"!"}, {"s"=>"$"}]
subs.repeated_combination(subs.size)
.map {|e| string.gsub(/./) {|c| (g = e.find {|h| h.key?(c)}) ? g[c] : c}}
.uniq
#=> ["this is @ test", "th!s !s @ test", "thi$ i$ @ te$t", "th!$ !$ @ te$t",
# "th!s !s a test", "th!$ !$ a te$t", thi$ i$ a te$t"]
Explanation:
a = subs.repeated_combination(subs.size)
# Enumerator...
a.to_a
# [[{"a"=>"@"},{"a"=>"@"},{"a"=>"@"}], [{"a"=>"@"},{"a"=>"@"},{"i"=>"!"}],
# [{"a"=>"@"},{"a"=>"@"},{"s"=>"$"}], [{"a"=>"@"},{"i"=>"!"},{"i"=>"!"}],
# [{"a"=>"@"},{"i"=>"!"},{"s"=>"$"}], [{"a"=>"@"},{"s"=>"$"},{"s"=>"$"}],
# [{"i"=>"!"},{"i"=>"!"},{"i"=>"!"}], [{"i"=>"!"},{"i"=>"!"},{"s"=>"$"}],
# [{"i"=>"!"},{"s"=>"$"},{"s"=>"$"}], [{"s"=>"$"},{"s"=>"$"},{"s"=>"$"}]]
b = a.map {|e| string.gsub(/./) {|c| (g = e.find {|h| h.key?(c)}) ? g[c] : c}}
#=> ["this is @ test", "th!s !s @ test", "thi$ i$ @ te$t", "th!s !s @ test",
# "th!$ !$ @ te$t", "thi$ i$ @ te$t", "th!s !s a test", "th!$ !$ a te$t",
# "th!$ !$ a te$t", "thi$ i$ a te$t"]
To see how b is computed, consider the second element of a that is passed to the block:
e = [{"a"=>"@"},{"a"=>"@"},{"i"=>"!"}]
Because of the regex, /./, gsub passes each character c of string to the block
{|c| (g = e.find {|h| h.key?(c)}) ? g[c] : c}
A search is made of e to determine if any of the three hashes has c as a key. If one is found, namely, g, the character c is replaced with g[c]; else, the character is left unchanged.
Notice that the first two elements of e are the same. Efficiency could be improved by changing the first line to:
subs.repeated_combination(subs.size).map(&:uniq)
but efficiency is not one of the virtues of this approach.
Returning to the main calculation, the final step is:
b.uniq
#=> ["this is @ test", "th!s !s @ test", "thi$ i$ @ te$t", "th!$ !$ @ te$t",
# "th!s !s a test", "th!$ !$ a te$t", "thi$ i$ a te$t"]