像Array#map一样轻松映射Hash

时间:2017-02-09 19:45:42

标签: ruby hash

有时我想映射一个集合。 如果它是一个阵列很容易:

foo = [1,2,3]
foo.map {|v| v + 1}
#=> [2, 3, 4]

但哈希的工作原理并不相同:

bar = {a: 1, b: 2, c: 3}
bar.map{|k,v| v+1}
#=> [2, 3, 4]

我真正喜欢的是:

bar = {a: 1, b: 2, c: 3}
bar.baz{|k,v| v+1}
#=> {:a=>2, :b=>3, :c=>4}

其中Hash#baz是某种方法。是否有一种简单的方法来获得类似地图的"哈希的体验?

5 个答案:

答案 0 :(得分:10)

在Ruby 2.4中,您可以使用内置的Hash#transform_values

bar = {a: 1, b: 2, c: 3}
# => {:a=>1, :b=>2, :c=>3}
bar.transform_values {|v| v+1 }
# => {:a=>2, :b=>3, :c=>4}

答案 1 :(得分:4)

ruby-forum,您可以使用Hash#merge将哈希与自身合并:

bar = {a: 1, b: 2, c: 3}
#=> {:a=>1, :b=>2, :c=>3}
bar.merge(bar){|k,v| v+1}
#=> {:a=>2, :b=>3, :c=>4}

答案 2 :(得分:3)

关于Ruby的一个好处是,如果你不喜欢Ruby核心中的内容,你总是可以去扩展它来填补缺失的部分:

class Hash
  def map_values
    map do |k,v|
      [ k, yield(k, v) ]
    end.to_h
  end
end

它为您提供了您想要的东西:

bar = {a: 1, b: 2, c: 3}
# => {:a=>1, :b=>2, :c=>3}
bar.map_values{ |k,v| v+1 }
# => {:a=>2, :b=>3, :c=>4}

我很惊讶这并没有被引入核心Ruby,但它可能在未来。

更新:正如Eric指出的那样,transform_values现在在Ruby 2.4.0中。如果您使用的是Rails 4.2或更高版本,那么这也在ActiveSupport中。

bar = {a: 1, b: 2, c: 3}
# => {:a=>1, :b=>2, :c=>3}
bar.transform_values{ |v| v+1 }
# => {:a=>2, :b=>3, :c=>4}

答案 3 :(得分:2)

只是指出解决这一需求的明显且最常见的解决方案:

bar = {a: 1, b: 2, c: 3}
# => {:a=>1, :b=>2, :c=>3}
bar.map { |k, v| [k, v + 1] }.to_h
# => {:a=>2, :b=>3, :c=>4}

答案 4 :(得分:1)

bar = {a: 1, b: 2, c: 3}

bar.merge(bar) { |*,v| v+1 }
  #=> {:a=>2, :b=>3, :c=>4}

这使用Hash.merge的形式,它使用一个块来返回合并的两个哈希中存在的键的值,这里是所有键。

另一种方式:

bar.keys.each { |k| bar[k] += 1 }
bar
  #=> {:a=>2, :b=>3, :c=>4}

可以使用Object#tap在一行中写入:

bar.tap { |h| h.keys.each { |k| h[k] += 1 } }
  #=> {:a=>2, :b=>3, :c=>4}