如何更改哈希值?


126

我想用替换value哈希中的每个value.some_method

例如,对于给定的简单哈希:

{"a" => "b", "c" => "d"}` 

每个值都应为.upcased,因此它看起来像:

{"a" => "B", "c" => "D"}

我试过了#collect#map但始终只能返回数组。有没有一种优雅的方法可以做到这一点?

更新

该死的,我忘了:哈希在实例变量中,不应更改。我需要一个具有更改后的值的新哈希,但不希望不显式定义该变量,然后循环遍历填充它的哈希。就像是:

new_hash = hash.magic{ ... }

我的答案的第二个示例返回一个新的哈希。如果您希望我进一步介绍#inject所做的魔术,请对此发表评论。
kch

Answers:


218
my_hash.each { |k, v| my_hash[k] = v.upcase } 

或者,如果您希望以非破坏性的方式进行操作,则返回一个新的哈希而不是修改my_hash

a_new_hash = my_hash.inject({}) { |h, (k, v)| h[k] = v.upcase; h } 

最新版本的另一个好处是您也可以转换键。


26
第一个建议不合适。在迭代时修改可枚举元素会导致未定义的行为-这在其他语言中也有效。这是Matz的回复(他回复了使用Array的示例,但答案是通用的):j.mp/tPOh2U
Marcus

4
@Marcus我同意通常应该避免这种修改。但是在这种情况下,它可能是安全的,因为修改不会更改将来的迭代。现在,如果分配了另一个键(不是传递给该块的那个键),那么将会有麻烦。
开尔文2012年

36
@Adam @kch each_with_objectinject您不希望使用哈希结束块的更方便的版本:my_hash.each_with_object({}) {|(k, v), h| h[k] = v.upcase }
Kelvin

6
还有一种非常简洁的语法,可以将成对的列表转换为哈希,因此您可以编写a_new_hash = Hash[ my_hash.map { |k, v| [k, v.upcase] } ]
重写

2
使用Ruby 2,您可以编写.to_h
roman-roman


35

您可以收集值,然后将其从Array再次转换为Hash。

像这样:

config = Hash[ config.collect {|k,v| [k, v.upcase] } ]

23

这样做:

my_hash.each_with_object({}) { |(key, value), hash| hash[key] = value.upcase }

inject与优点相反的是,您无需在块内再次返回哈希值。


我喜欢这种方法不会修改原始哈希。
Christian Di Lorenzo

10

试试这个功能:

h = {"a" => "b", "c" => "d"}
h.each{|i,j| j.upcase!} # now contains {"a" => "B", "c" => "D"}.

5
很好,但是应该注意的是,它仅在j具有对自己起作用的破坏性方法时才有效。因此,它无法处理一般的value.some_method情况。
kch

很好,随着他的更新,您的第二个解决方案(非破坏性)是最好的。
克里斯·多格特

10

ActiveSupport v4.2.0中提供了一种方法。叫做transform_values并且基本上只为每个键值对执行一个块。

因为他们使用a做,所以each我认为没有比循环更好的方法了。

hash = {sample: 'gach'}

result = {}
hash.each do |key, value|
  result[key] = do_stuff(value)
end

更新:

从Ruby 2.4.0开始,您可以本地使用#transform_values#transform_values!


2

您可能想更进一步,并在嵌套哈希上执行此操作。当然,在Rails项目中这种情况相当多。

以下是确保params哈希在UTF-8中的一些代码:

  def convert_hash hash
    hash.inject({}) do |h,(k,v)|
      if v.kind_of? String
        h[k] = to_utf8(v) 
      else
        h[k] = convert_hash(v)
      end
      h
    end      
  end    

  # Iconv UTF-8 helper
  # Converts strings into valid UTF-8
  #
  # @param [String] untrusted_string the string to convert to UTF-8
  # @return [String] your string in UTF-8
  def to_utf8 untrusted_string=""
    ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
    ic.iconv(untrusted_string + ' ')[0..-2]
  end  

1

我做这样的事情:

new_hash =哈希[* original_hash.collect {| key,value | [key,value + 1]}。flatten]

这也为您提供了通过任何表达式转换键或值的功能(当然,它是非破坏性的)。


1

Ruby有该tap方法(1.8.71.9.32.1.0这对像这样的东西非常有用)。

original_hash = { :a => 'a', :b => 'b' }
original_hash.clone.tap{ |h| h.each{ |k,v| h[k] = v.upcase } }
# => {:a=>"A", :b=>"B"}
original_hash # => {:a=>"a", :b=>"b"}

1

特定于Rails

如果某人只需要对to_s每个值调用方法并且不使用Rails 4.2(包括transform_values方法link),则可以执行以下操作:

original_hash = { :a => 'a', :b => BigDecimal('23.4') }
#=> {:a=>"a", :b=>#<BigDecimal:5c03a00,'0.234E2',18(18)>}
JSON(original_hash.to_json)
#=> {"a"=>"a", "b"=>"23.4"}

注意:需要使用“ json”库。

注意2:这也会将键转换为字符串


1

如果您知道这些值是字符串,则可以调用replace在循环中对它们方法:通过这种方式,您可以更改值。

Altohugh限于值是字符串的情况,因此不能完全回答问题,我认为这对某人可能有用。


1
new_hash = old_hash.merge(old_hash) do |_key, value, _value|
             value.upcase
           end

# old_hash = {"a" => "b", "c" => "d"}
# new_hash = {"a" => "B", "c" => "D"}

这样有效吗?
ioquatix
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.