All possible combinations of selected character substitution in a string in ruby
string = "this is a test"
subs = ['a'=>'@','i'=>'!','s'=>'$']
subs = subs.first.map(&:to_a)
1.upto(subs.length).each do |n|
subs.combination(n).each do |a|
p a.each_with_object(string.dup){|pair, s| s.gsub!(*pair)}
end
end
All possible combinations of selected character substitution in a string in ruby - Improved
Rather than your subs
, the following one, which includes the information that replacement can vacuously apply, would be easier to handle:
subs = {"a" => ["a", "@"], "i" => ["i", "!"], "s" => ["s", "$", "&"]}
Using this, you should have sticked to my answer. The following is just a minor modification to my answer to your previous question:
string = "this is a test"
a = subs.values
a = a.first.product(*a.drop(1))
a.each do |a|
p [subs.keys, a].transpose.each_with_object(string.dup){|pair, s| s.gsub!(*pair)}
end
which gives:
"this is a test"
"thi$ i$ a te$t"
"thi& i& a te&t"
"th!s !s a test"
"th!$ !$ a te$t"
"th!& !& a te&t"
"this is @ test"
"thi$ i$ @ te$t"
"thi& i& @ te&t"
"th!s !s @ test"
"th!$ !$ @ te$t"
"th!& !& @ te&t"
All possible permutations or combinations of a selected character substitution's in a string in ruby w/ permutations
Is this what you wanted?
string = "this is a test"
p = ?a, ?i, ?s
q = ?@, ?!, [ ?$, ?& ]
replacements = Hash.new { |h, e| Array e }.tap do |h|
p.zip( q ).each { |p, q| h[p] = p, *Array( q ) }
end
#=> {"a"=>["a", "@"], "i"=>["i", "!"], "s"=>["s", "$", "&"]}
puts string.split( '' ).map( &replacements.method( :[] ) ).reduce( &:product ).map { |e|
e.flatten.join
}
Algorithm to find all combinations of a string, maintain order, not fixed length
Here are two ways it could be done without making use of Array#combination
. I've also included code for the case when combination
is permitted (#3)1.
1. Map each of the numbers between 1
and 2**n-1
(n
being the length of the string) to a unique combination of characters from the string
def string_combinations(str)
arr = str.chars
(1..2**str.length-1).map do |n|
pos = n.bit_length.times.map.with_object([]) { |i,a| a << i if n[i] == 1 }
arr.values_at(*pos).join
end.sort
end
string_combinations("wxyz")
# => ["w", "wx", "wxy", "wxyz", "wxz", "wy", "wyz", "wz",
# "x", "xy", "xyz", "xz", "y", "yz", "z"]
Discrete probability theory provides us with the equation
sum(i = 1 to n) ( |i| C(n,i) ) == 2^n - 1
where C(n,i)
is "the number of combinations of n things taken i at a time".
If the given string is "wxyz"
, n = "wxyz".length #=> 4
, so there are 2**4 - 1 #=> 15
combinations of one or more characters from this string. Now consider any of the numbers between 1 and 16, say 11, which is 0b1011
in binary. Converting this to an array of binary digits, we obtain:
bin_arr = [1,0,1,1]
We now pluck out each character of wxyz
for which the corresponding index position in bin_arr
equals 1
, namely
["w", "y", "z"]
and then join those elements to form a string:
["w", "y", "z"].join #=> "wyz"
Since each number 1
to 15
corresponds to a distinct combination of one or more characters from this string, , we can obtain all such combinations by repeating the above calculation for each the numbers between 1
and 15
.
No matter which method you use, the resulting array will contain 2**n - 1
elements, so you are looking at O(2**str.length)
.
2. Use recursion
def string_combinations(str)
(combos(str) - [""]).sort
end
def combos(str)
return [str, ""] if str.length==1
forward = combos str[1..-1]
[*forward, *[str[0]].product(forward).map(&:join)]
end
string_combinations("wxyz")
# => ["w", "wx", "wxy", "wxyz", "wxz", "wy", "wyz", "wz",
# "x", "xy", "xyz", "xz", "y", "yz", "z"]
Notice that
combos("wxyz")
#=> ["z", "", "yz", "y", "xz", "x", "xyz", "xy",
# "wz", "w", "wyz", "wy", "wxz", "wx", "wxyz", "wxy"]
includes an empty string, which must be removed, and the array needs sorting. Hence the need to separate out the recursive method combos
.
3. Use Array#combination
Here we invoke arr.combination(n)
for all values of n
between 1
and arr.size
and return a (flattened) array comprised of all n
return values.
def string_combinations(str)
a = str.chars
(1..str.size).flat_map { |n| a.combination(n).map(&:join) }.sort
end
string_combinations "wxyz"
# => ["w", "wx", "wxy", "wxyz", "wxz", "wy", "wyz", "wz",
# "x", "xy", "xyz", "xz", "y", "yz", "z"]
1 Since I wrote it before realizing that's not what the OP wanted. ¯\_(ツ)_/¯
Generate all possible consecutive word combinations of a string
This solution avoids unnecessary joins
:
word = "hello"
size = word.size
min_size = 3
(min_size..size).flat_map { |l| (0..size - l).map { |i| word[i, l] } }
#=> ["hel", "ell", "llo", "hell", "ello", "hello"]
If you don't need an Array but just need to iterate over every possible substring, this solution will use less memory :
(min_size..size).each do |l|
(0..size - l).each do |i|
# do something with word[i, l]
end
end
Show all substring replacement options in Ruby
I'd do as below :
my_string = "yellow dogs are cooler than brown cats"
substitutions = {"yellow" => "black", "brown" => "grey"}
keys = substitutions.keys
arry_of_comb = 0.upto(substitutions.size).flat_map do |num|
keys.combination(num).map do |ary|
my_string.gsub(/\b#{Regexp.union(ary)}\b/,substitutions)
end
end
arry_of_comb
# => ["yellow dogs are cooler than brown cats",
# "black dogs are cooler than brown cats",
# "yellow dogs are cooler than grey cats",
# "black dogs are cooler than grey cats"]
Ruby string substitution with multiple options
def gen_products(swap, str)
swap_all = Hash.new { |_,k| [k] }.merge(swap)
arr = swap_all.values_at(*str.chars)
arr.shift.product(*arr).map(&:join)
end
See Hash::new (with a block), Hash#values_at and Array#product. If h = Hash.new { |_,k| [k] }
and h
does not have a key k
, h[k]
returns [k]
.
swap = { 'a'=>['$', '%', '^'], 'b'=>['3'], 'c'=>['4', '@'] }
gen_products(swap, "abc")
#=> ["$34", "$3@", "%34", "%3@", "^34", "^3@"]
Here
swap_all = Hash.new { |_,k| [k] }.merge(swap)
#=> {"a"=>["$", "%", "^"], "b"=>["3"], "c"=>["4", "@"]}
vals = swap_all.values_at(*str.chars)
#=> [["$", "%", "^"], ["3"], ["4", "@"]]
Another example:
gen_products(swap, "bca")
#=> ["34$", "34%", "34^", "3@$", "3@%", "3@^"]
and one more:
gen_products(swap, "axbycx")
#=> ["$x3y4x", "$x3y@x", "%x3y4x", "%x3y@x", "^x3y4x", "^x3y@x"]
Here
swap_all = Hash.new { |_,k| [k] }.merge(swap)
#=> {"a"=>["$", "%", "^"], "b"=>["3"], "c"=>["4", "@"]}
vals = swap_all.values_at(*str.chars)
#=> [["$", "%", "^"], ["x"], ["3"], ["y"], ["4", "@"], ["x"]]
ruby - How can I generate an array of every combination of letters and numbers of a given length?
alphanum = [*?a..?z, *?0..?9]
length.times.flat_map { |l|
alphanum.repeated_permutation(l + 1).map(&:join)
}
Note that length > 3
will give you a lot of results.
EDIT: As meagar says, this is very memory-intensive. An Enumerator-based answer (not as pretty, but won't kill your memory):
e = Enumerator.new do |y|
length.times do |l|
alphanum.repeated_permutation(l + 1).each do |p|
y << p.join
end
end
end
Ruby - replace characters in a string with a symbol
What about gsub(/\S/, '*')
It will find all non-whitespace characters and replace every one of them with *
. \S
is a regex character class matching non-whitespace chars (thanks @jdno).
E.g.
pry> "as12 43-".gsub(/\S/, '*')
=> "**** ***"
So in your case:
def change_word(word)
word.gsub(/\S/, '*')
end
You may also extract the regex outside of the method to optimize it a bit:
CHANGE_WORD_PATTERN = /\S/
def change_word(word)
word.gsub(CHANGE_WORD_PATTERN, '*')
end
Related Topics
How to Use a Ruby Block to Assign Variables in Chef Recipe
How to Use "_Blank" or "_New" in Rails
Tcp Socket Communication Between Processes on Heroku Worker Dyno
How to Execute Windows Cli Commands in Ruby
No Such File to Load -- SQLite3/Sqlite3_Native
Ruby: Could Not Find a Temporary Directory
How to Get Http Headers Before Downloading with Ruby's Openuri
Best Way to Use Twitter Bootstrap Icons as Links in Ruby on Rails 3
How to Decode Q-Encoded Strings in Ruby
What Order Do Before Filters Occur In
Ruby What Class Gets a Method When There Is No Explicit Receiver
How to Get Sinatra to Work with Httpclient
New to Ruby - While Loop Issues in Irb
Ruby Attr_Accessor VS. Getter/Setter Benchmark: Why Is Accessor Faster
Opening Several Threads with Watir-Webdriver Results in 'Connection Refused' Error
How to Split a String by Commas Except Inside Parenthesis, Using a Regular Expression