zip vs transpose

Posted by Dr Nic on October 03, 2006

Prelude

If I wanted to create a Hash from two arrays - one containing the keys and the other the values - I have used transpose:

>> keys = %w(name description country)
>> values = ["Dr Nic", "Good lookin'", "Netherlands"]
>> [keys, values].transpose
=> [["name", "Dr Nic"], ["description", "Good lookin'"], ["country", "Netherlands"]]
>> hash = Hash[*[keys, values].transpose.flatten]
=> {"name"=>"Dr Nic", "country"=>"Netherlands", "description"=>"Good lookin'"}

I was happy with that.

The newcomer from outta town…

Today I found zip for Arrays.

>> keys.zip(values)
=> [["name", "Dr Nic"], ["description", "Good lookin'"], ["country", "Netherlands"]]
>> hash = Hash[*keys.zip(values).flatten]
=> {"name"=>"Dr Nic", "country"=>"Netherlands", "description"=>"Good lookin'"}

Now I don’t know which is better. Nor do I know which syntax is cleaner and more meaningful.

Both are as meaningless as each other:

  • How many people remember what transpose means from matrix mathematics at school anyway?
  • How many people would look at keys.zip(values) and guess what the result will be?

Anyone with any thoughts on this?

Trackbacks

Use this link to trackback from your own site.

Comments

Leave a response

  1. Paul Battley Tue, 03 Oct 2006 13:39:31 UTC

    I think that zip, like inject, is one of those Ruby methods whose name and function is inspired by another language. In my case, I didn’t start using it in Ruby until I came across it in Haskell.

    If you think about it, the name ‘zip’ does make some sense - it’s like the teeth of a zip (zipper) meshing together - but it’s not necessarily the image that springs to mind when you first encounter it.

  2. Dr Nic Tue, 03 Oct 2006 13:57:53 UTC

    Perhaps an alias interlace would be helpful.

  3. Paul Battley Tue, 03 Oct 2006 17:25:52 UTC

    Déjà vu! #ruby-lang IRC logs from 2005 (ThreeDayMonk is me):

    [ThreeDayMonk] I can think of many ways to turn a1 = %w[foo bar baz]; a2 = [1,2,3] into {’foo’=>1, ‘bar’=>2, ‘baz’=>3}, but I’m wondering if there’s not a really simple way
    [chris2] Hash[*a1.zip(a2)] ?
    [ThreeDayMonk] ah, zip interleaves them? that’s what I couldn’t remember
    [chris2] i think so
    [chris2] maybe you need a flatten
    [chris2] yeah
    [ThreeDayMonk] yes
    [chris2] Hash[*[1,2,3].zip([4,5,6]).flatten]
    [chris2] which gets us to the question why Hash[*hash.to_a] doesnt work :)
    [ThreeDayMonk] I really wish that some of the buitl-in methods had more obvious names - it would take me less time to type ‘interleave’ than to work out that ‘zip’ is the method I need
    [Ben] I would probably go for [a1, a2].transpose
    [Ben] .flatten
    [ThreeDayMonk] Ben: yeah, transpose is nice
    [ThreeDayMonk] it has a matrix parallel
    [chris2] ThreeDayMonk: but it’s called zip in haskell too! :P
    [ThreeDayMonk] all the more reason not to do it :-)
    [ThreeDayMonk] actually, I kind of get it
    [ThreeDayMonk] like the teeth of a zip
    [chris2] yeah
    [chris2] makes perfect sense. you just need to know it exists
    [chris2] it’s not really interleave
    [ThreeDayMonk] interlace would be more accurate

  4. Greg Spurrier Tue, 03 Oct 2006 19:21:10 UTC

    I prefer the zip version. It’s easier for me to get my head around “zip these two things together” than treating arrays like vectors and doing matrix operations on them.

    But, I like it even better when I don’t have to think about it at all. Why not just extend the Hash class with a from_keys_and_values class method?

    class Hash
    def Hash.from_keys_and_values(k_array, v_array)
    Hash[*k_array.zip(v_array).flatten]
    end
    end

    Then it’s just:

    irb(main):011:0> k = %w(a b c)
    => ["a", "b", "c"]
    irb(main):012:0> v = [1, 2, 3]
    => [1, 2, 3]
    irb(main):013:0> Hash.from_keys_and_values(k,v)
    => {”a”=>1, “b”=>2, “c”=>3}

  5. Dr Nic Tue, 03 Oct 2006 19:38:04 UTC

    @greg - I agree that this is a great solution for readability sake.

    @paul - “interlace” IS such a nice name :)

  6. Bob Hutchison Wed, 04 Oct 2006 14:32:36 UTC

    Zip also takes a block like so:

    irb(main):001:0> (1..3).zip(4..6) { | a, b | puts “a: #{a}, b: #{b}”; a + b }
    a: 1, b: 4
    a: 2, b: 5
    a: 3, b: 6
    => nil

    This can be handy.

  7. Dr Nic Wed, 04 Oct 2006 14:40:47 UTC

    @bob - ooh nice. That’s +1 for zip.

  8. evan Fri, 06 Oct 2006 21:27:29 UTC

    Hey, I never thought of it as a zipper. That’s great. I would use zip with some frequency but just sort of in an intuitive way. I didn’t have any rigorous explanation of what it did.

    To me, “zip” in software context means compression. Thus the confusion.

  9. AegisThorne Tue, 02 Jan 2007 06:00:28 UTC

    I’m fond of

    class Array
    def hash_with(values)
    Hash[*self.zip(values).flatten]
    end
    end

    [1,2,3].hash_with(["2", "3","4"])
    => {1=>”2″, 2=>”3″, 3=>”4″}

Comments