Hash invert in Ruby?

后端 未结 8 1783
星月不相逢
星月不相逢 2021-01-13 00:27

I\'ve got a hash of the format:

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

and I want to end up with:

{ a => key1, b =         


        
相关标签:
8条回答
  • 2021-01-13 00:54

    Ok, let's guess. You say you have an array but I agree with Benoit that what you probably have is a hash. A functional approach:

     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}
    

    Also:

     h.map { |k, vs| Hash[vs.product([k])] }.inject(:merge)
     #=> {"a"=>:key1, "b"=>:key1, "c"=>:key1, "d"=>:key2, "e"=>:key2, "f"=>:key2}
    
    0 讨论(0)
  • 2021-01-13 00:56

    If you want to correctly deal with duplicate values, then you should use the Hash#inverse from Facets of Ruby

    Hash#inverse preserves duplicate values, e.g. it ensures that hash.inverse.inverse == hash

    either:

    • use Hash#inverse from here: http://www.unixgods.org/Ruby/invert_hash.html

    • use Hash#inverse from FacetsOfRuby library 'facets'

    usage like this:

    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} 
    

    The code looks like this:

    # 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} 
    
    0 讨论(0)
  • 2021-01-13 01:01

    In the case where a value corresponds to more than one key, like "c" in this example...

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

    ...some of the other answers will not give the expected result. We will need the reversed hash to store the keys in arrays, like so:

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

    This should do the trick:

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

    This was my use case, and I would have defined my problem much the same way as the OP (in fact, a search for a similar phrase got me here), so I suspect this answer may help other searchers.

    0 讨论(0)
  • 2021-01-13 01:03
    hash = {:key1 => ["a", "b", "c"], :key2 => ["d", "e", "f"]}
    

    first variant

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

    or

    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, your hash is:

    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 讨论(0)
  • 2021-01-13 01:09
    new_hash={}
    hash = {"key1" => ['a', 'b', 'c'], "key2" => ['d','e','f']}
    hash.each_pair{|key, val|val.each{|v| new_hash[v] = key }}
    

    This gives

    new_hash # {"a"=>"key1", "b"=>"key1", "c"=>"key1", "d"=>"key2", "e"=>"key2", "f"=>"key2"}
    
    0 讨论(0)
  • 2021-01-13 01:11

    If you're looking to reverse a hash formatted like this, the following may help you:

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

    this returns:

    {"a"=>:key1, "b"=>:key1, "c"=>:key1, "d"=>:key2, "e"=>:key2, "f"=>:key2}
    
    0 讨论(0)
提交回复
热议问题