4
\$\begingroup\$

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 & Code Golf
4

Output

Prog
mmar
ing 
zzuP
les 
oC &
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:

HELLO, WORLD!

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(&:join)
     .join("\n")
end

puts twist("Hello, World!", 5)
puts
puts twist("foo bar baz", 7)
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

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.

\$\endgroup\$
5
  • \$\begingroup\$ I like this answer, just a pair of questions: I would replace text.length.fdiv(chunk_size).ceil * chunk_size with text.length + text.length % chunk_size which one would be better? Could you please explain the interpolated regex approach? I do not understand it \$\endgroup\$
    – Caridorc
    Aug 24, 2015 at 7:21
  • \$\begingroup\$ @Caridorc Yours is better - should've figured that one myself, but I was a little too tired, I guess :) As for the regex: If chunk_size is 4, the regex ends up being .{4} (or .{1,4} in the last code block). That basically means "match 4 characters of any kind" (or "match 1-4 characters..."). The %r/.../ is just Ruby syntax for declaring a regex pattern, but it allows you to use #{..} interpolation, like you can with strings. You can also use %r[..] or %r(..) or %r{..}- I picked slashes for readability. Similar to the %w syntax for split-on-whitespace lists. Make sense? \$\endgroup\$
    – Flambino
    Aug 24, 2015 at 8:05
  • \$\begingroup\$ More clear now, the same as .scan(Regexp("." * chunk_size)) rigth? \$\endgroup\$
    – Caridorc
    Aug 24, 2015 at 8:09
  • \$\begingroup\$ @Caridorc Yup, exactly \$\endgroup\$
    – Flambino
    Aug 24, 2015 at 8:09
  • \$\begingroup\$ Perfect, I will wait a day or two before accepting. \$\endgroup\$
    – Caridorc
    Aug 24, 2015 at 8:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.