2011-09-22 42 views
6

Tôi đã có một hash của các định dạng:Hash đảo ngược trong Ruby?

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

và tôi muốn kết thúc với:

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

cách dễ nhất đạt được điều này là gì?

Tôi đang sử dụng Ruby on Rails.

CẬP NHẬT

OK Tôi quản lý để trích xuất các đối tượng thực từ nhật ký máy chủ, nó đang được đẩy qua AJAX.

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

Tôi không chắc chắn cách làm cho nó in ra một mảng để tôi có thể đọc nó để thử nghiệm. – cjm2671

+0

Tuy nhiên, bạn đã thử những gì? 'p mảng' in ra. – Mat

+1

Bạn có chắc chắn đó là một mảng chứ không phải là một băm? Cách bạn mô tả nó là mơ hồ. –

Trả lời

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

biến thể đầu tiên

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

hoặc

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, băm của bạn là:

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

Điều này có vẻ đúng nhưng không thể làm cho nó hoạt động được; Tôi đã cập nhật bài đăng ngay bây giờ để hiển thị đối tượng thực tế. – cjm2671

+0

xem cập nhật của tôi .. – fl00r

+0

như chúng tôi đã thảo luận trong bạn câu hỏi trước, fl00r, sử dụng tiêm khi không có nhu cầu cho nó là IMHO không phải là một lựa chọn tốt. Ngoài ra, bạn đang sử dụng bản đồ nhưng thực sự làm các hiệu ứng phụ, điều này gây nhầm lẫn, mỗi cái đều tốt hơn (tốt, "tốt hơn", mỗi cái thường là xấu). Vâng, tôi biết, tôi là một chức năng-bigot :-) – tokland

1

Nếu bạn đang tìm kiếm để đảo ngược một hash được định dạng như thế này, sau đây có thể giúp bạn:

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

này trả về:

{"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 }} 

Điều này cho phép

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

Ok, chúng ta hãy đoán. Bạn nói rằng bạn có một mảng nhưng tôi đồng ý với Benoit rằng những gì bạn có thể có là một băm. Một cách tiếp cận chức năng:

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} 

Ngoài ra:

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

Chỉ hoạt động với Ruby (> =) 1.9. –

+0

@undur_gongor: đoạn đầu tiên nên hoạt động trong 1.8 – tokland

+0

Tôi tiếp tục nhận được "số lẻ đối số cho Hash'". 'Hash [vs ...]' phải là 'Hash [* vs.map {| v | [v, k]} .flatten] 'và' inject (: merge) 'chỉ là 1,9. Dù sao, điều này chỉ chứng minh tôi nên chuyển sang 1.9 :-) –

0

Một cách để đạt được những gì bạn đang tìm kiếm:

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

Nếu bạn muốn để đối phó một cách chính xác với giá trị nhân bản, sau đó bạn nên sử dụng Hash # inverse từ Facets of Ruby

Hash#inverse duy trì các giá trị trùng lặp, ví dụ:nó đảm bảo rằng hash.inverse.inverse == hash

một trong hai:

sử dụng 'mặt' như thế này :

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} 

Mã này trông như thế này:

# 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

Trong trường hợp một giá trị tương ứng với nhiều hơn một khóa, như "c" trong ví dụ này ...

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

... một số các câu trả lời khác sẽ không đưa ra kết quả mong đợi. Chúng ta sẽ cần băm đảo ngược để lưu trữ các phím trong mảng, như vậy:

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

này nên làm như lừa:

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

Đây là trường hợp sử dụng của tôi, và tôi sẽ đã xác định vấn đề của tôi nhiều giống như cách OP (trên thực tế, tìm kiếm một cụm từ tương tự đã đưa tôi đến đây), vì vậy tôi nghi ngờ câu trả lời này có thể giúp những người tìm kiếm khác.

3

Rất nhiều câu trả lời hay khác. Chỉ muốn quăng cái này trong quá cho Ruby 2.0 và 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} 

này được tận dụng: Hash::[]Enumerable#flat_map

Cũng trong các phiên bản mới có Enumerable::each_with_object đó rất giống với Enumerable::inject/Enumerable::reduce :

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

Performing một cách nhanh chóng benchmark (Ruby 2.0.0p0; 2012 Macbook Air) sử dụng một băm ban đầu với 100 phím, mỗi với 100 giá trị khác biệt:

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 

Cho biết biến thể each_with_object nhanh hơn cho tập dữ liệu đó.

+0

cảm ơn, Aaron, đã dạy tôi #flat_map –