【问题标题】:How to change Hash values?如何更改哈希值?
【发布时间】:2018-10-16 22:55:36
【问题描述】:

我想用value.some_method 替换散列中的每个value

例如,给定一个简单的哈希:

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

每个值都应该是.upcased,所以看起来像:

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

我尝试了#collect#map,但总是只取回数组。有没有优雅的方法来做到这一点?

更新

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

new_hash = hash.magic{ ... }

【问题讨论】:

  • 我回答中的第二个示例返回一个新的哈希值。如果你想让我扩展#inject 正在做的魔法,请评论它。

标签: ruby syntax hash


【解决方案1】:
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 } 

最后一个版本还有一个额外的好处,就是您也可以转换密钥。

【讨论】:

  • 第一个建议不合适;在迭代时修改可枚举元素会导致未定义的行为——这在其他语言中也有效。这是 Matz 的回复(他回复了一个使用数组的示例,但答案是通用的):j.mp/tPOh2U
  • @Marcus 我同意一般情况下应避免此类修改。但在这种情况下,它可能是安全的,因为修改不会改变未来的迭代。现在,如果分配了另一个键(不是传递给块的那个),那么就会有麻烦。
  • @Adam @kch each_with_objectinject 的更方便的版本,当您不想用哈希结束块时:my_hash.each_with_object({}) {|(k, v), h| h[k] = v.upcase }
  • 还有一个非常简洁的语法可以将一对列表转换成哈希,所以你可以写a_new_hash = Hash[ my_hash.map { |k, v| [k, v.upcase] } ]
  • 使用 Ruby 2 你可以写.to_h
【解决方案2】:

由于ruby 2.4.0可以使用原生的Hash#transform_values方法:

hash = {"a" => "b", "c" => "d"}
new_hash = hash.transform_values(&:upcase)
# => {"a" => "B", "c" => "D"}

还有破坏性的Hash#transform_values!版本。

【讨论】:

【解决方案3】:

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

像这样:

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

【讨论】:

    【解决方案4】:

    这样就可以了:

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

    inject 相比,优点是您无需在块内再次返回散列。

    【讨论】:

    • 我喜欢这种方法不修改原始哈希。
    【解决方案5】:

    ActiveSupport v4.2.0 中有一个方法。它被称为transform_values,基本上只是为每个键值对执行一个块。

    由于他们使用each 进行操作,我认为没有比循环遍历更好的方法了。

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

    更新:

    从 Ruby 2.4.0 开始,您可以原生使用 #transform_values#transform_values!

    【讨论】:

      【解决方案6】:

      试试这个功能:

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

      【讨论】:

      • 这很好,但应该注意的是,它仅适用于当 j 具有对自身起作用的破坏性方法时。所以它不能处理一般的 value.some_method 情况。
      • 好点,随着他的更新,您的第二个解决方案(非破坏性)是最好的。
      【解决方案7】:

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

      这里有一些代码可以确保参数哈希是 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  
      

      【讨论】:

        【解决方案8】:

        我会这样做:

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

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

        【讨论】:

          【解决方案9】:

          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"}
          

          【讨论】:

            【解决方案10】:

            特定于 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:这也会将键转换为字符串

            【讨论】:

              【解决方案11】:

              如果你知道值是字符串,你可以在循环中对它们调用replace方法:这样你就可以改变值。

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

              【讨论】:

                【解决方案12】:
                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"}
                

                【讨论】:

                • 这样有效率吗?
                猜你喜欢
                • 2012-05-27
                • 2012-04-27
                • 2015-01-26
                • 2021-12-07
                • 2013-08-22
                • 1970-01-01
                • 2017-05-11
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多