Home > database >  Output is given in 2 lines after concatenation of elements with odd and even indexes in a given stri
Output is given in 2 lines after concatenation of elements with odd and even indexes in a given stri

Time:05-18

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.

  • Related