Skip to content

Instantly share code, notes, and snippets.

@jasonLaster
Created September 23, 2012 20:51
Show Gist options
  • Save jasonLaster/3773009 to your computer and use it in GitHub Desktop.
Save jasonLaster/3773009 to your computer and use it in GitHub Desktop.
string transposition

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment