【问题标题】:How to convert only strings hash values that are numbers to integers如何仅将数字的字符串哈希值转换为整数
【发布时间】:2012-09-01 07:58:35
【问题描述】:

我有从几个不同的 XML 数据库转储导入的散列行,看起来像这样(但具有不同的键):

{"Id"=>"1", "Name"=>"Cat", "Description"=>"Feline", "Count"=>"123"}

我尝试使用#to_i,但它会将非数字字符串转换为0

"Feline".to_i
# => 0

但我想要的是让"Feline" 保持字符串,而在上面的示例中IdCount 成为整数1123

有没有一种简单的方法可以将数字字符串值转换为整数?

【问题讨论】:

    标签: ruby string integer type-conversion


    【解决方案1】:

    一行答案: 使用正则表达式方法

    h.merge(h) { |k, v| v.match(/\A[+-]?\d+?(\.\d+)?\Z/) ? v.to_i : v }
    

    使用整数方法

    h.merge(h) { |k, v| Integer(v) rescue v }
    

    【讨论】:

    • @SergioTulentsev 没问题,任何改进都会被接受:)
    • 在我的私有库中,我实际上在哈希上定义了#do_with_values 方法,所以在我的代码中,我只想说 h.do_with_values { |v|整数 v 救援 v }.
    • @BorisStitnicky 我认为您的回答已经足够了。我的只是补充:)
    【解决方案2】:

    使用Kernel#Integer:

    my_hash = {"Id"=>"1", "Name"=>"Cat", "Description"=>"Feline", "Count"=>"123"}
    Hash[ my_hash.map{ |a, b| [ a,
                                begin
                                  Integer b
                                rescue ArgumentError
                                  b
                                end ] } ]
    

    稍后添加:使用我的 y_support gem,您可以使哈希运算更加简洁。

    require 'y_support/core_ext/hash'
    my_hash.with_values { |v| begin
                                Integer b
                              rescue ArgumentError
                                b
                              end }
    

    YSupport 可以由gem install y_support 安装,还提供Hash#with_keysHash#with_values!Hash#with_keys! 可以执行您期望的操作,以及Hash#modify 期望二进制块返回一对值,就地修改哈希。已经有人提议在未来将此类方法直接添加到 Ruby 核心中。

    【讨论】:

    • 这让我很感兴趣。你能说得更具体点吗?
    • +1。很好,谢谢鲍里斯。我喜欢 Brad 的正则表达式和三元组,虽然这样更快。
    • 提案:my_hash.each_pair { | k, v | my_hash[k] = Integer(v) rescue v }
    • 不仅如此,这也避免了自己解析整数的需要。想象一下,您想要转换为浮点数,其中的字符串可以是“-3.4e-19”。
    • @undur_gongor:谢谢。我以为你讨厌我的括号分组。我尽量避免只使用尾随救援,因为它不允许您指定要救援的错误,但在这种情况下,我认为没关系。另外,我通常避免在块中提及集合本身的名称,我会用它来迭代它。
    【解决方案3】:

    我想你知道哪些字段应该是整数(你的消费代码可能取决于它),所以我建议你转换特定的字段。

    c = Hash[h.map { |k,v| [k, %w(Id Count).include?(k) ? Integer(v) : v ] }]
    

    【讨论】:

    • 是的,虽然我也很懒,而且是数据驱动逻辑的粉丝,所以我可以在其他 xml 上复制算法,而不必担心字段名称与数据类型的匹配。将字段映射到数据类型会在我的算法中硬编码我想要避免的模式,并且还会引入更多的错误点和胖手指。 DNRY。
    【解决方案4】:

    我有一个类似问题要解决,农药分析结果以异类(糟糕的设计!)格式进入系统...负整数作为特殊代码(未检测到,未测试,未量化等...),nil 作为未检测到的同义词,浮动表示量化的化合物和字符串表示通过/失败 boolean...别着急,这是 10 岁-在生产环境中运行,永远不会新建高度修补的应用程序;)

    我从顶级语言学家那里学到的两件事

    0) DON'T ITERATE-MODIFY AN ENUMERABLE (return a copy)

    1) YOUR REGEX WON'T COVER ALL CASES

    虽然我不是rescue 的忠实粉丝,但我认为它符合保持代码整洁的目的。所以,我一直在用它来减轻我的输入:

    ha = { 
     "p_permethrin" => nil, 
     "p_acequinocyl"=>"0.124", 
     "p_captan"=>"2.12", 
     "p_cypermethrin"=>"-6", 
     "p_cyfluthrin"=>"-6",
     "p_fenhexamid"=>"-1", 
     "p_spinetoram"=>"-6", 
     "p_pentachloronitrobenzene"=>"-6", 
     "p_zpass"=>"true"
    }
    
    Hash[ha.map{|k,v| [k, (Float(v) rescue v)]}] # allows nil
    Hash[ha.map{|k,v| [k, (Float(v) rescue v.to_s)]}] # nit to empty string
    

    我什至会

    class Hash
      # return a copy of the hash, where values are evaluated as Integer and Float
      def evaluate_values
        Hash[self.map{|k,v| [k, (Float(v) rescue v)]}]
      end
    end
    

    【讨论】:

    • 感谢 mekdigital - 我认为在尝试解决此类问题时,实用主义有很多话要说。有时偷偷摸摸的rescue 正是我们所需要的。
    【解决方案5】:

    使用正则表达式和三元运算符,您可以将其合并到某处的逻辑中:

    string =~ /^\d+$/ ? string.to_i : string
    

    【讨论】:

    • 虽然我认为鲍里斯的回答更纯粹,但我对正则表达式和三元运算符情有独钟。我怀疑鲍里斯的开始/救援会更快......
    • “有些人在遇到问题时会想‘我知道,我会使用正则表达式’。现在他们有两个问题。” (Jamie Zawinski) "-42" 怎么样?您的方法会将"abcd\n2\nefgh" 转换为0。为什么不使用恰好针对给定问题存在的Integer()
    【解决方案6】:

    这不仅会处理整数,还会处理所有数字。

    my_hash = {"Id"=>"1", "Name"=>"Cat", "Description"=>"Feline", "Count"=>"123"}
    
    result = my_hash.inject({}) { |result,(key,value)|
        if value.match(/^\s*[+-]?((\d+_?)*\d+(\.(\d+_?)*\d+)?|\.(\d+_?)*\d+)(\s*|([eE][+-]?(\d+_?)*\d+)\s*)$/)
                result[key.to_sym] = value.to_i
        else
                result[key.to_sym] = value
        end
        result
    }
    

    感谢Determine if a string is a valid float value 提供正则表达式

    【讨论】:

    • 呃,注入,一个古老的关键字:)。你在哪里撒的?
    • 现在,这太过分了 :) 除非你是由 LOC 支付的:)
    • 您的正则表达式对于多行字符串失败。 my_hash = {"Id"=>"1", "Name"=>"Cat", "Description"=>"Feline\n42", "Count"=>"123"} 导致 {:Id=>1, :Name=>"Cat", :Description=>0, :Count=>123}
    【解决方案7】:

    为String定义一个新方法:String#to_number

    class String
      def to_number
        Integer(self) rescue Float(self) rescue self
      end
    end
    

    测试一下:

    "1".to_number => 1
    "Cat".to_number => "Cat"
    

    【讨论】:

      猜你喜欢
      • 2018-04-17
      • 2015-04-26
      • 1970-01-01
      • 2019-08-26
      • 2014-08-22
      • 1970-01-01
      • 1970-01-01
      • 2011-01-27
      • 1970-01-01
      相关资源
      最近更新 更多