I wrote a function which takes all elements with odd indexes and concatenates them with all elements with even indexes in a given string n times:
def encrypt(text,n)
while n > 0
n -= 1
str_array = text.split("")
even_values = str_array.select.with_index { |_, i| i.even? }
odd_values = str_array.select.with_index { |_, i| i.odd? }
text = (odd_values even_values).join
end
puts text
end
puts 'Type your message to encrypt:'
text = gets
puts 'Type number of times to run encryption:'
n = gets.to_i
encrypt(text,n)
The issue is that in case the input text has odd number of elements the function will return a text in 2 lines. At the same time if number of elements of input text is even the output is a 1 line text which is what I want.
Console output with 10 elements in input text:
Type your message to encrypt:
abcdefghij
Type number of times to run encryption:
1
bdfhjacegi
Console output with 11 elements in input text:
Type your message to encrypt:
abcdefghijk
Type number of times to run encryption:
1
bdfhj
acegik
CodePudding user response:
For n = 1
def doit(str)
enum = [false, true].cycle
str.each_char.partition { enum.next }.map(&:join).join
end
doit "abcdefghij"
#=> "bdfhjacegi"
doit "abcdefghijk"
#=> "bdfhjacegik"
For n > 1
n.times.reduce(str) { |s,_| doit(s) }
For example:
4.times.reduce(str) { |s,_| doit(s) }
#=>"ejdichbgafk"
See Enumerable#reduce (a.k.a. inject
)
Now consider the calculations performed by doit
.
First consider the enumerator enum
, created with the method Array#cycle:
enum = [false, true].cycle
#=> #<Enumerator: [false, true]:cycle>
We can use the method Enumerator#next to see what values this enumerator enum
will generate:
enum.next
#=> false
enum.next
#=> true
enum.next
#=> false
... ad infinitum
Now let's reset the enumerator using Enumerator#rewind:
enum.rewind
#<Enumerator: [false, true]:cycle>
Suppose
str = "abcdefghijk"
Then
e = str.each_char
#=> #<Enumerator: "abcdefghijk":each_char>
We can convert this enumerator to an array to see what elements it will generate.
e.to_a
#=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"]
Continuing,
a = e.partition { enum.next }
#=> [["b", "d", "f", "h", "j"], ["a", "c", "e", "g", "i", "k"]]
b = a.map(&:join)
#=> ["bdfhj", "acegik"]
b.join
#=> "bdfhjacegik"
Note a.map(&:join)
is shorthand for a.map { |arr| arr.join }
.
See String#each_char, Enumerable#partition and Array#join.