2011-09-22 148 views
6

我已經得到了格式的哈希:在Ruby中哈希顛倒?

{key1 => [a, b, c], key2 => [d, e, f]} 

,我想直到結束:

{ a => key1, b => key1, c => key1, d => key2 ... } 

什麼是實現這一目標的最簡單的方法?

我正在使用Ruby on Rails。

UPDATE

OK,我設法提取從服務器日誌真正的對象,它正在通過AJAX推。

Parameters: {"status"=>{"1"=>["1", "14"], "2"=>["7", "12", "8", "13"]}} 
+0

我不知道如何讓它打印出一個數組,以便我可以讀取它以便進行實驗。 – cjm2671

+0

不過,你有什麼嘗試? 'p array'打印出來。 – Mat

+1

你確定它是一個數組而不是哈希?你描述它的方式是不明確的。 –

回答

7
hash = {:key1 => ["a", "b", "c"], :key2 => ["d", "e", "f"]} 

第一變型

hash.map{|k, v| v.map{|f| {f => k}}}.flatten 
#=> [{"a"=>:key1}, {"b"=>:key1}, {"c"=>:key1}, {"d"=>:key2}, {"e"=>:key2}, {"f"=>:key2}] 

hash.inject({}){|h, (k,v)| v.map{|f| h[f] = k}; h} 
#=> {"a"=>:key1, "b"=>:key1, "c"=>:key1, "d"=>:key2, "e"=>:key2, "f"=>:key2} 

UPD

OK,你的哈希:

hash = {"status"=>{"1"=>["1", "14"], "2"=>["7", "12", "8", "13"]}} 
hash["status"].inject({}){|h, (k,v)| v.map{|f| h[f] = k}; h} 
#=> {"12"=>"2", "7"=>"2", "13"=>"2", "8"=>"2", "14"=>"1", "1"=>"1"} 
+0

這看起來不錯,但不能完全正常工作;我現在更新了帖子以顯示實際的對象。 – cjm2671

+0

看到我的更新.. – fl00r

+0

正如我們在你前面的問題fl00r中討論的那樣,在沒有必要的時候使用注入,恕我直言不是一個好的選擇。此外,你正在使用地圖,但實際上是做副作用,這是令人困惑的,每一個都更好(好,「更好」,每個通常都很糟糕)。是的,我知道,我是一個功能偏執的人:-) – tokland

1

如果你正在尋找扭轉此格式的哈希,下面可以幫助你:

a = {:key1 => ["a", "b", "c"], :key2 => ["d", "e", "f"]} 
a.inject({}) do |memo, (key, values)| 
    values.each {|value| memo[value] = key } 
    memo 
end 

這將返回:

{"a"=>:key1, "b"=>:key1, "c"=>:key1, "d"=>:key2, "e"=>:key2, "f"=>:key2} 
1
new_hash={} 
hash = {"key1" => ['a', 'b', 'c'], "key2" => ['d','e','f']} 
hash.each_pair{|key, val|val.each{|v| new_hash[v] = key }} 

這給

new_hash # {"a"=>"key1", "b"=>"key1", "c"=>"key1", "d"=>"key2", "e"=>"key2", "f"=>"key2"} 
2

好的,我們來猜吧。你說你有一個陣列,但我同意Benoit,你可能有一個哈希。一種功能性的方法:

h = {:key1 => ["a", "b", "c"], :key2 => ["d", "e", "f"]} 
h.map { |k, vs| Hash[vs.map { |v| [v, k] }] }.inject(:merge) 
#=> {"a"=>:key1, "b"=>:key1, "c"=>:key1, "d"=>:key2, "e"=>:key2, "f"=>:key2} 

另外:

h.map { |k, vs| Hash[vs.product([k])] }.inject(:merge) 
#=> {"a"=>:key1, "b"=>:key1, "c"=>:key1, "d"=>:key2, "e"=>:key2, "f"=>:key2} 
+0

這僅適用於Ruby(> =)1.9。 –

+0

@undur_gongor:第一個片段應該工作在1.8 – tokland

+0

我不斷收到「'散列值爲奇數個參數」。 'Hash [vs ...]'應該是'Hash [* vs.map {| v | [v,k]} .flatten]'和'inject(:merge)'只有1.9。無論如何,這只是證明我應該切換到1.9 :-) –

0

一種方式來實現你在找什麼:

arr = [{["k1"] => ["a", "b", "c"]}, {["k2"] => ["d", "e", "f"]}] 

results_arr = [] 
arr.each do |hsh| 
    hsh.values.flatten.each do |val| 
    results_arr << { [val] => hsh.keys.first }··· 
    end 
end 


Result: [{["a"]=>["k1"]}, {["b"]=>["k1"]}, {["c"]=>["k1"]}, {["d"]=>["k2"]}, {["e"]=>["k2"]}, {["f"]=>["k2"]}] 
1

如果你想有重複值的正確處理,那麼你應該使用來自紅寶石刻面的哈希#

Hash#inverse保留重複值, 例如它確保了hash.inverse.inverse == hash

之一:

使用這樣的逆:

require 'facets' 

h = {:key1 => [:a, :b, :c], :key2 => [:d, :e, :f]} 
=> {:key1=>[:a, :b, :c], :key2=>[:d, :e, :f]} 

h.inverse 
=> {:a=>:key1, :b=>:key1, :c=>:key1, :d=>:key2, :e=>:key2, :f=>:key2} 

的代碼看起來是這樣的:

# this doesn't looks quite as elegant as the other solutions here, 
# but if you call inverse twice, it will preserve the elements of the original hash 

# true inversion of Ruby Hash/preserves all elements in original hash 
# e.g. hash.inverse.inverse ~ h 

class Hash 

    def inverse 
    i = Hash.new 
    self.each_pair{ |k,v| 
     if (v.class == Array) 
     v.each{ |x| 
      i[x] = i.has_key?(x) ? [k,i[x]].flatten : k 
     } 
     else 
     i[v] = i.has_key?(v) ? [k,i[v]].flatten : k 
     end 
    } 
    return i 
    end 

end 


h = {:key1 => [:a, :b, :c], :key2 => [:d, :e, :f]} 
=> {:key1=>[:a, :b, :c], :key2=>[:d, :e, :f]} 

h.inverse 
=> {:a=>:key1, :b=>:key1, :c=>:key1, :d=>:key2, :e=>:key2, :f=>:key2} 
2

在一個值對應於一個以上的鍵,如在這個例子中的「c」的情況下...

{ :key1 => ["a", "b", "c"], :key2 => ["c", "d", "e"]} 

...一些的其他答案不會給出預期的結果。我們將需要扭轉哈希鍵存儲陣列,像這樣:

{ "a" => [:key1], "b" => [:key1], "c" => [:key1, :key2], "d" => [:key2], "e" => [:key2] } 

這應該做的伎倆:

reverse = {} 
hash.each{ |k,vs| 
    vs.each{ |v| 
     reverse[v] ||= [] 
     reverse[v] << k 
    } 
} 

這是我的使用情況下,我會多定義我的問題與OP相同的方式(實際上,搜索一個類似的詞組讓我在這裏),所以我懷疑這個答案可能會幫助其他搜索者。

3

很多其他的好答案。只是想折騰太這一個對Ruby 2.0和1.9.3:

hash = {apple: [1, 14], orange: [7, 12, 8, 13]} 

Hash[hash.flat_map{ |k, v| v.map{ |i| [i, k] } }] 
# => {1=>:apple, 14=>:apple, 7=>:orange, 12=>:orange, 8=>:orange, 13=>:orange} 

這是利用:Hash::[]Enumerable#flat_map

而且在這些新版本有Enumerable::each_with_object這是非常相似的Enumerable::inject/Enumerable::reduce

hash.each_with_object(Hash.new){ |(k, v), inverse| 
    v.each{ |e| inverse[e] = k } 
} 

執行快速benchmark(紅寶石2.0.0p0; 2012的Macbook Air)使用原始散列與100鍵,每個鍵與100個不同的值:

Hash::[] w/ Enumerable#flat_map 
      155.7 (±9.0%) i/s -  780 in 5.066286s 
Enumerable#each_with_object w/ Enumerable#each 
      199.7 (±21.0%) i/s -  940 in 5.068926s 

顯示each_with_object變體對於該數據集更快。

+0

謝謝,亞倫,教我#flat_map –