I completely agree with you that my to_block
method for transposing strings lacked clarity. It actually began as this ruby doozy of a line.
def to_block1(lines)
lines.map!(&:chars).shift.zip(&:lines).map(&:join)
end
Ideally, I would like the answer to be as simple as lines.zip
, which would just transpose the line chars. Unfortunately, ruby strings are not enumerable and zip
expects to be called on the first enumerable and passsed the 2nd through nth enumerable.
If you let zip take an array of enumerables, which I think is reasonable in ruby because it is so OO, then to_block
becomes much more readable.
def to_block2(lines)
lines.map(&:chars).ezip.map(&:join)
end
module Enumerable
def ezip
first,*rest = self
first.zip *rest
end
end
The other opportunity for improvement would be to make ruby strings enumerable. All this takes in ruby is including the Enumerable module and implementing each
. Now, ezip can take the array of strings without calling chars
on each line.
def to_block3(lines)
lines.ezip.map(&:join)
end
class String
include Enumerable
def each(&block)
self.chars.each do |c|
block.call(c)
end
end
end
module Enumerable
def ezip
first,*rest = self
first.zip *rest
end
end
Ofcourse, both of these "improvements" are local improvements. The one line to_block
solution is simpler, but making strings enumerable could have introduced bugs in the existing codebase and adding a new Enumerable method makes the code-base more idiosyncratic. Probably not worth it in the long run.
I think there's a really simple way to solve both the enumerable and zip problems with a little bit simpler code.
def to_block4(lines)
first, *rest = lines.map(&:chars)
first.zip(rest).map(&:join)
end
What I like about this code is that the first line makes it easy to see that the lines are now enumerable and partitioned first and rest. The second line, then interleaves the lines together and joins them together. The second line is also as simple as the simplest optimized solution, but it doesnt have all the overhead of making strings enumerable and adding ezip.