ruby 如何在散列中交换键和值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10989259/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 05:12:14  来源:igfitidea点击:

How to swap keys and values in a hash

rubyhashmap

提问by Jonathan Allard

How do I swap keys and values in a Hash?

如何交换哈希中的键和值?

I have the following Hash:

我有以下哈希:

{:a=>:one, :b=>:two, :c=>:three}

that I want to transform into:

我想变成:

{:one=>:a, :two=>:b, :three=>:c}

Using mapseems rather tedious. Is there a shorter solution?

使用起来map似乎相当乏味。有更短的解决方案吗?

回答by Nigel Thorne

Ruby has a helper method for Hash that lets you treat a Hash as if it was inverted (in essence, by letting you access keys through values):

Ruby 有一个 Hash 的辅助方法,可以让您将 Hash 视为反转(本质上,通过让您通过值访问键):

{a: 1, b: 2, c: 3}.key(1)
=> :a

If you want to keep the inverted hash, then Hash#invertshould work for most situations:

如果您想保留反向散列,那么Hash#invert应该适用于大多数情况:

{a: 1, b: 2, c: 3}.invert
=> {1=>:a, 2=>:b, 3=>:c}

BUT...

但...

If you have duplicate values, invertwill discard all but the last occurrence of your values (because it will keep replacing new value for that key during iteration). Likewise, keywill only return the first match:

如果您有重复的值,invert将丢弃除最后一次出现的值之外的所有值(因为它会在迭代期间不断替换该键的新值)。同样,key只会返回第一个匹配项:

{a: 1, b: 2, c: 2}.key(2)
=> :b

{a: 1, b: 2, c: 2}.invert
=> {1=>:a, 2=>:c}

So, if your values are unique you can use Hash#invert. If not, then you can keep all the values as an array, like this:

因此,如果您的值是唯一的,则可以使用Hash#invert. 如果没有,那么您可以将所有值保留为一个数组,如下所示:

class Hash
  # like invert but not lossy
  # {"one"=>1,"two"=>2, "1"=>1, "2"=>2}.inverse => {1=>["one", "1"], 2=>["two", "2"]} 
  def safe_invert
    each_with_object({}) do |(key,value),out| 
      out[value] ||= []
      out[value] << key
    end
  end
end

Note: This code with tests is now on GitHub.

注意:这个带有测试的代码现在在 GitHub 上

Or:

或者:

class Hash
  def safe_invert
    self.each_with_object({}){|(k,v),o|(o[v]||=[])<<k}
  end
end

回答by Jonathan Allard

You bet there is one! There is always a shorter way to do things in Ruby!

你打赌有一个!在 Ruby 中总是有一种更短的方式来做事!

It's pretty simple, just use Hash#invert:

这很简单,只需使用Hash#invert

{a: :one, b: :two, c: :three}.invert
=> {:one=>:a, :two=>:b, :three=>:c}

Et voilà!

等等!

回答by Riaze

files = {
  'Input.txt' => 'Randy',
  'Code.py' => 'Stan',
  'Output.txt' => 'Randy'
}

h = Hash.new{|h,k| h[k] = []} # Create hash that defaults unknown keys to empty an empty list
files.map {|k,v| h[v]<< k} #append each key to the list at a known value
puts h

This will handle the duplicate values too.

这也将处理重复值。

回答by dawg

If you have a hash where are the keys are unique, you can use Hash#invert:

如果您有一个哈希,其中键是唯一的,您可以使用Hash#invert

> {a: 1, b: 2, c: 3}.invert
=> {1=>:a, 2=>:b, 3=>:c} 

That won't work if you have non unique keys, however, where only the last keys seen will be kept:

但是,如果您有非唯一键,这将不起作用,但是,只会保留最后看到的键:

> {a: 1, b: 2, c: 3, d: 3, e: 2, f: 1}.invert
=> {1=>:f, 2=>:e, 3=>:d}

If you have a hash with non unique keys, you might do:

如果你有一个非唯一键的哈希,你可以这样做:

> hash={a: 1, b: 2, c: 3, d: 3, e: 2, f: 1}
> hash.each_with_object(Hash.new { |h,k| h[k]=[] }) {|(k,v), h| 
            h[v] << k
            }     
=> {1=>[:a, :f], 2=>[:b, :e], 3=>[:c, :d]}

If the values of the hash are already arrays, you can do:

如果散列的值已经是数组,则可以执行以下操作:

> hash={ "A" => [14, 15, 16], "B" => [17, 15], "C" => [35, 15] }
> hash.each_with_object(Hash.new { |h,k| h[k]=[] }) {|(k,v), h| 
            v.map {|t| h[t] << k}
            }   
=> {14=>["A"], 15=>["A", "B", "C"], 16=>["A"], 17=>["B"], 35=>["C"]}

回答by Tilo

# 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

Hash#inversegives you:

Hash#inverse给你:

 h = {a: 1, b: 2, c: 2}
 h.inverse
  => {1=>:a, 2=>[:c, :b]}
 h.inverse.inverse
  => {:a=>1, :c=>2, :b=>2}  # order might not be preserved
 h.inverse.inverse == h
  => true                   # true-ish because order might change

whereas the built-in invertmethod is just broken:

而内置invert方法刚刚被破坏:

 h.invert
  => {1=>:a, 2=>:c}    # FAIL
 h.invert.invert == h 
  => false             # FAIL

回答by Prashant Ravi Darshan

Using Array

使用数组

input = {:key1=>"value1", :key2=>"value2", :key3=>"value3", :key4=>"value4", :key5=>"value5"}
output = Hash[input.to_a.map{|m| m.reverse}]

Using Hash

使用哈希

input = {:key1=>"value1", :key2=>"value2", :key3=>"value3", :key4=>"value4", :key5=>"value5"}
output = input.invert