2011-09-22 94 views
2

我需要這個哈希哈希數組轉換

{"peter" => ["apple", "orange", "mango"], "sandra" => ["flowers", "bike"]} 

轉換到這個陣列:

[["peter", "apple"], ["peter", "orange"], ["peter", "mango"], ["sandra", "flowers"], ["sandra", "bike"]] 

現在我有這個解決方案

my_hash.inject([]){|ar, (k,v)| ar << v.map{|c| [k,c]}}.flatten(1) 

但我相信這裏是更優雅與那些ziptranspose magick :)解決方案:

回答

5

您正確的辨識Enumerable#inject的方案是正確的。在Ruby中,inject/reduce有點濫用,如果它們適合手頭的問題,我們必須小心並選擇正確的抽象(地圖,選擇,拉鍊,拼合等)。在這種情況下:

h = {"peter" => ["apple", "orange", "mango"], "sandra" => ["flowers", "bike"]} 
h.map { |k, vs| vs.map { |v| [k, v] } }.flatten(1) 
#=> [["peter", "apple"], ["peter", "orange"], ["peter", "mango"], ["sandra", "flowers"], ["sandra", "bike"]] 

但是,如果你想使用Enumerable#zip不要讓任何人阻止你;-)

h.map { |k, vs| [k].cycle(vs.size).zip(vs) }.flatten(1) 

而作爲@steenslag說,也:

h.map { |k, vs| [k].product(vs) }.flatten(1) 

所以最後我們可以這樣寫:

h.flat_map { |k, vs| [k].product(vs) } 
+0

托克蘭,這是作弊!他要求'zip'或'轉置'magick! **編輯:**對不起,您在編輯此評論前23秒編輯了它。 – Serabe

+1

@serabe,我認爲fl00r只是提到了一些功能結構,並不是他特別想要一個基於zip的解決方案。我的第二個片段只是爲了好玩,第一個可能更清晰。 – tokland

+0

@Serabe,tokland是對的,我想要更優雅的解決方案:) – fl00r

1

隨着zip

hash.inject([]){ |ar, (k,v)| ar << ([k]*v.size).zip(v) } 

一個合理的解決方案使用transpose太:

[ 
    hash.keys.map{|k| [k]*hash[k].size }.flatten, 
    hash.keys.map{|k| hash[k] }.flatten 
].transpose 

要考慮到:

  1. hash.keys應該在這兩種情況下返回鍵以相同的順序,所以不要在其他語言中使用它,除非您確信這一點。
  2. 我會去第一個選項。
2
h.inject([]){|a,(k,vs)| a+vs.map {|v| [k,v]}} 

你也可以,因爲它使用相同的列表,而不是建立在每個迭代一個新的使用這個版本

h.inject([]){|a,(k,vs)| a+=vs.map {|v| [k,v]}} 

這是最有效的。然而,它感覺不對(對我來說)使用inject並修改了一個變量。 each版本也會這樣做。

a = []; h.each {|k,vs| a+=vs.map {|v| [k,v]}} 

它略短且具有表現力。