2011-09-22 139 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 –