Dr Nic

zip vs transpose

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?

Related posts:

  1. map_by_method now works with ActiveRecord associations I was always annoyed that map_by_method was broken for ActiveRecord...
  2. My Year in Cities, 2006 This game seemed like fun, so I thought I’d play....
  3. I love “map by pluralisation” [now: map_by_method] Update: this is a gem called map_by_method. The other day...
  4. Composite migrations for Composite Primary Keys The good people at Err-the-Blog were almost satisfied with the...

9 Responses to “zip vs transpose”

  1. Paul Battley says:

    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 says:

    Perhaps an alias interlace would be helpful.

  3. Paul Battley says:

    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. 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 says:

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

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

  6. 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 says:

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

  8. evan says:

    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 says:

    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″}