如何交换散列中的键和值?

我有以下内容:

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

我想转化成:

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

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

推荐答案

Ruby有一个Hash的helper方法,可以让你把Hash当作是反向的(本质上是通过允许你通过值访问键):

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

如果要保留倒排哈希,那么Hash#invert应该适用于大多数情况:

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

BUT...

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

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

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

所以,如果你的值是唯一的,你可以使用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

注意:这个带有测试的代码现在是on GitHub.

或者:

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

Ruby相关问答推荐

安全导航运算符的使用是否应该在两种情况下进行单元测试(对象存在 + 对象无)?

Ruby 避免类方法并保持其类型系统简单优雅的秘诀

如何解密在 sjcl.js 中使用 ruby​​ 创建的 AES-256-GCM

在 Ruby 中,如何编写一个可以与 &:symbol 一起使用的简单方法?

如何从 DateTime 值中删除区域?

.nil?、.blank? 之间的区别?和.empty?

有人可以解释 Ruby 在块中使用管道字符吗?

我在哪里可以找到积极开发的 Ruby lint 工具?

类中的 Ruby 类(或模块中的模块)

为 Ruby 模块中的每个方法调用执行代码

如何判断我是从 JRuby 还是 Ruby 运行?

如何删除字符串中的重复空格?

class_eval、class_exec、module_eval 和 module_exec 有什么区别?

从字符串中删除所有特殊字符 - ruby

如何在没有 Rails 的情况下使用 RSpec?

如何让 Ruby 1.9 成为 Ubuntu 上的默认 Ruby?

向实例对象添加方法

以小时为单位的时差

严格将字符串转换为整数(或 nil)

JavaScript Array:获取项目的范围