Twisting words! (boustrophedon formatting) I decided to implement properly the Code Golf challenge about twisting strings. I report the assignement verbatim for convenience:


  Twisting Words!
  
  Given a string and a positive integer. You must twist the string, back
  and forth.
  
  Example Input / Output
  
  
  
  Input

Programming Puzzles &amp; Code Golf
4

  
  Output

Prog
mmar
ing 
zzuP
les 
oC &amp;
de G
 flo

  
  Input
  
  The input can be taken in through STDIN, or function argument. The
  input will consist of a string and a positive integer, n. The
  integer will determine the length of each twisted line.
  
  The string is twisted back-and-forth. An input of HELLO, WORLD! and
  5 would look like:
  
  
  
  Output
  
  The output will be the twisted text. It may not any trailing
  whitespace. If the input string length is not divisible be the line
  length, add a space until the line is filled:
  
  An example of this:    Input

Hello, World!
5

  
  Output (Note the whitespace at the very end)

Hello
roW ,
ld!



My implementation



def twist(text, chunk_size)
   text
     .+(" " * ((text.length % chunk_size) - 1))
     .chars
     .each_slice(chunk_size)
     .each_with_index
     .map {|str, i| i.odd? ? str.reverse : str}
     .map(&amp;:join)
     .join("\n")
end

puts twist("Hello, World!", 5)
puts
puts twist("foo bar baz", 7)
Looks fine to me. Only thing I'd change is the way the padding is done: You can use ljust instead, which seems a little more appropriate to me. Something like:

.ljust(text.length.fdiv(chunk_size).ceil * chunk_size, " ")


You can also use the with_index modifier on map instead of doing a separate each_with_index, i.e.:

.map.with_index { |chunk, index| index.odd? ? chunk.reverse : chunk }




Of course there are several approaches to this. For instance, you needn't necessarily use chars and then join. For instance:

def twist(text, chunk_size)
  text
    .ljust(text.length.fdiv(chunk_size).ceil * chunk_size, " ")
    .scan(%r/.{#{chunk_size}}/)
    .map.with_index { |chunk, index| index.odd? ? chunk.reverse : chunk }
    .join("\n")
end


I won't say it's a better solution, though. Fewer lines, sure, but the interpolated regex isn't pretty. But again, it's just to show an alternative approach.

You can also move the ljust, to avoid the long'ish bit of arithmetic:

def twist(text, chunk_size)
  text
    .scan(%r/.{1,#{chunk_size}}/)
    .map { |chunk| chunk.ljust(chunk_size, " ") }
    .map.with_index { |chunk, index| index.odd? ? chunk.reverse : chunk }
    .join("\n")
end


It's obviously it's kinda brute-force, since we know that it'd only be the last chunk that could require padding. But again: Just an alternative.