- loading...
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
transposemeans 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:
- map_by_method now works with ActiveRecord associations I was always annoyed that map_by_method was broken for ActiveRecord...
- My Year in Cities, 2006 This game seemed like fun, so I thought I’d play....
- I love “map by pluralisation” [now: map_by_method] Update: this is a gem called map_by_method. The other day...
- Composite migrations for Composite Primary Keys The good people at Err-the-Blog were almost satisfied with the...
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.
Perhaps an alias
interlacewould be helpful.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!
[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
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}
@greg – I agree that this is a great solution for readability sake.
@paul – “interlace” IS such a nice name
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.
@bob – ooh nice. That’s +1 for zip.
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.
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″}