Array
我想你可能寫過以下這樣的程式。其中 params[:a] 可以只有一個元素,也可以是陣列。但是為了接下來能夠處理,我們需要轉成陣列 array 變數:
array = (params[:a].is_a? Array)? params[:a] : [params[:a]]
這裡我們手動判斷了 params[:a] 是不是陣列,實在是有點 ugly。其實 Ruby 內建的 API 就可以支援下述寫法:
array = Array(params[:a])
無論 params[:a] 是陣列還是單一元素,Array(params[:a])
會確保出來一定是陣列。
Hash
你有一個物件或是陣列,你想要轉成 Hash,最常見基本的作法會是先初始一個 hash,然後迭代設定它:
hash = {}
data.each { |d| hash[d.foo] = d.bar }
高級一點的,也許會思考怎樣寫成一行,然後想到用 inject:
hash = data.inject({}) { |h,d| h[d.foo] = d.bar; h }
不過,這裡我要介紹一種我的最愛:
hash = Hash[ data.map {|d| [d.foo, d.bar]} ]
Hash[]
是一個Ruby內建的API可以把陣列轉成Hash,而且效能非常好,比前兩個方法都好。inject
想當然是最慢的,我最不推薦使用。
有人跟我抱怨Hash[]
有點 magic 可讀性不佳。可是啊,這是 Ruby “原生”的 Hash API,一點都不 magic。你不知道看不懂跟抱怨程式碼可讀性不佳,我個人認為是兩件事情哩。
其實理論上 inject (fold) 會比較快,因為只有一次線性搜尋,
而 Hash 因為有一次 map 又一次 Hash, 需要兩次線性搜尋,
再加上有 intermediate 的 array 產生,成本應該是高不少。
稍微跑了 benchmark, 在 MRI 中 inject (fold) 只稍微快一點點,
我想原因就像你說的,由於 Hash 是由 C 實作的,所以有優勢在。
不過在 Rubinius 中就差很多了,inject (fold) 快了一倍。
有趣的是,JRuby 實在是比其他的快很多,而且真的是
Hash+map 比較快,在 –server 下差得不少。
以下單位是秒,在我的電腦上。
MRI:
inject(fold): 6.23
Hash+map: 6.5
Rubinius:
inject(fold): 14.51
Hash+map: 27.51
JRuby: (不可思議地快)
inject(fold): 2.47
Hash+map: 2.29
測資 *5 下的 JRuby:
inject(fold): 16.38
Hash+map: 16.6
all code and result: gist.github.com/1065318
hash = Hash[ data.map {|d| [d.foo, d.bar]} ]
After I tried it, get the result that is not what I want
我试了这个方法后,得到的怎么不是想要的结果;
data = Trainer.all(:limit => 3)
h2 = data.inject({}) { |h,d| h[d.id] = d.login; h}
=> {36097=>”jhill153″, 37632=>”rhenderson165″, 36100=>”snaazir156″}
h3 = Hash[ data.map {|t| [t.id,t.login]} ]
=>{[37632, “rhenderson165”]=>[37632, “rhenderson165”], [36097, “jhill153”]=>[36097, “jhill153”], [36100, “snaazir156”]=>[36100, “snaazir156”]}
is it my fault?
是我错了吗?
@denny:
I can’t reproduce your problem :(
I try and get the same correct result. (Ruby 1.8.7 and 1.9.2)
momolog.info/2010/10/07/ruby-map-array-to-hash/
does it your blog too