【问题标题】:Hash invert in Ruby?Ruby中的哈希反转?
【发布时间】:2011-09-22 11:04:17
【问题描述】:

我有一个哈希格式:

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

我想最终得到:

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

实现这一目标的最简单方法是什么?

我正在使用 Ruby on Rails。

更新

好的,我设法从服务器日志中提取了真实对象,它正在通过 AJAX 推送。

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

【问题讨论】:

  • 我不知道如何让它打印出一个数组,以便我可以读取它以进行实验。
  • 仍然,您尝试过什么? p array 打印内容。
  • 你确定它是一个数组而不是一个哈希?你描述它的方式是模棱两可的。
  • 为什么要使用奇怪的伪代码?显示为 Ruby 对象...
  • 它更简单 -- 使用 FacetsOfRuby 中的 Hash#inverse -- 需要 'facets'

标签: ruby arrays hash


【解决方案1】:
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

好的,你的哈希是:

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

【讨论】:

  • 这看起来不错,但不能完全发挥作用;我现在更新了帖子以显示实际对象。
  • 正如我们在上一个问题中讨论的那样,fl00r,在不需要时使用注入,恕我直言,这不是一个好的选择。此外,您正在使用 map 但实际上会产生副作用,这令人困惑,每个都更好(嗯,“更好”,每个通常都不好)。是的,我知道,我是一个功能偏执狂:-)
【解决方案2】:

还有很多其他好的答案。只是想在 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 }
}

使用具有 100 个键的原始哈希执行快速 benchmark(Ruby 2.0.0p0;2012 Macbook Air),每个键具有 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 变体对于该数据集更快。

【讨论】:

    【解决方案3】:

    好吧,让我们猜猜。你说你有一个数组,但我同意 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}
    

    【讨论】:

    • 我不断收到“odd number of arguments for Hash”。 Hash[vs...] 应该是 Hash[ *vs.map { |v| [v, k] }.flatten ] 并且 inject(:merge) 仅是 1.9。无论如何,这只是证明我应该切换到 1.9 :-)
    • @undur_gongor,您是否有机会使用
    • 是的 (1.8.6)。我一直对此感到困惑。我现在如何修复错误的 cmets?
    • @undur_gongor:老cmets不用担心,人们可以看到整个对话。您可以删除它们,但似乎我在单独说话:-)
    【解决方案4】:

    如果一个值对应多个键,例如本例中的“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 大致相同的方式来定义我的问题(事实上,搜索类似短语让我来到了这里),所以我怀疑这个答案可能会对其他搜索者有所帮助。

    【讨论】:

      【解决方案5】:

      如果您希望反转这样格式的哈希,以下内容可能会对您有所帮助:

      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}
      

      【讨论】:

        【解决方案6】:
        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"}
        

        【讨论】:

          【解决方案7】:

          如果你想正确处理重复值,那么你应该使用 Hash#inverse 来自 Ruby 的方方面面

          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} 
          

          【讨论】:

            【解决方案8】:

            实现您想要的目标的一种方法:

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-04-23
              • 1970-01-01
              • 1970-01-01
              • 2015-04-12
              • 1970-01-01
              • 2017-07-06
              相关资源
              最近更新 更多