Ruby and elegance: Transpose

March 10, 2007

I recently found that the Array class of Ruby has a zip method, which works much like Haskell’s zip function, except that it allows multiple arguments.

For example:

$ irb --simple-prompt
>> [1,2,3].zip [4,5,6], [7,8,9]
=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

The night after I did this little experiment I suddenly realized: this is a transpose function! I quickly got out of bed to document my little discovery:

def transpose(matrix)
matrix[0].zip *matrix[1..-1]

It’s the little things that make me like this language so much.

Update: I forgot the asterisk before matrix[1..-1] in the above method.


  1. Very creative! When I first read about zip in Ruby, I thought “cool, but can’t think of a way to use it.” Then I read a book on Haskell, and the zip examples were an eye opener. The functions are not quite the same, but it still gave me ideas about using it.

  2. Did you know Python also has zip() and map()? I’m liking Python more and more, though I’m only now actually programming a little something in it myself.

  3. That’s http://docs.python.org/lib/built-in-funcs.html🙂

  4. % ri Array.transpose

    array.transpose -> an_array
    Assumes that _self_ is an array of arrays and transposes the rows and columns.

    a = [[1,2], [3,4], [5,6]]
    a.transpose #=> [[1, 3, 5], [2, 4, 6]]

  5. Sweet🙂

    It’s definitely shorter than GHC’s version in Haskell:

    transpose :: [[a]] -> [[a]]
    transpose [] = []
    transpose ([] : xss) = transpose xss
    transpose ((x:xs) : xss) = (x : [h | (h:t)

    See also: List.hs

  6. Impressive! Thank you.

  7. Here is another Haskell version of transpose:

    foldr (zipWith (:)) (repeat [])

  8. I tried this but didn’t end up with the same result as the first example. If I pass in an array of arrays as it expects, I get a different result:

    => [[1, [4, 5, 6]], [2, [7, 8, 9]], [3, [10, 11, 12]]]

    This is not the same as:
    [1,2,3].zip [4,5,6],[7,8,9],[10,11,12],[13,14,15]
    => [[1, 4, 7, 10, 13], [2, 5, 8, 11, 14], [3, 6, 9, 12, 15]]

    Am I going something wrong, am I passing in the matrix argument incorrectly?
    I want to be able to pass an array of arrays in and get the same result as calling the zip method directly on the first element of the matrix argument.

  9. In order to get the correct you need to add an asterisk:

    def transpose(matrix)
    matrix[0].zip matrix[*1..-1]

    which will unpack the array of arrays before processing.

    Also, A.nony.mouse is correct – Array has a transpose method that will render the same result:

    => [[1, 4, 7, 10, 13], [2, 5, 8, 11, 14], [3, 6, 9, 12, 15]]

  10. @Dom: Whoops, you’re right. I’ve updated the post. But I’d place that asterisk before matrix, otherwise it has a whole different meaning.

    Btw, the Array.transpose method is great! [[1,2,3],%w(a b c)].transpose is a beautiful syntax for zipping arrays.

  11. Hey Chris, deze Ruby-implementatie is dan wel korter maar doet niet hetzelfde als de Haskellimplementatie. Vergelijk:

    >> [1,2,3].zip [4,5,6], [7,8,9,10]
    => [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

    Prelude Data.List> transpose [[1,2,3], [4,5,6], [7,8,9,10]]

    Het feit dat Haskell goed omgaat met lijsten van verschillende lengte is extreem handig in de situaties waar ik transpose tot nu toe in gebruikt heb.

  12. One-line solution in Python:

    transpose = lambda matrix: zip(*matrix)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: