如何将仅为数字的字符串哈希值转换为整数

3

我有一些来自不同XML数据库转储的哈希行,看起来像这样(但键名不同):

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

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

"Feline".to_i
# => 0

但我希望的是,"Feline" 仍然是一个字符串,而上面例子中的 IdCount 变成整数 1123

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

7个回答

10

一句话回答: 使用正则表达式的方法。

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

使用整数方法

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

在我的私人库中,我实际上在哈希表上定义了#do_with_values方法,因此在我的代码中,我只需要说h.do_with_values { |v| Integer v rescue v }。 - Boris Stitnicky
@BorisStitnicky 我认为你的回答已经足够了。我的只是补充 :) - waldyr.ar

7

使用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宝石,您可以使哈希操作更加简洁。

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 核心直接添加这样的方法。


这让我很感兴趣。你能具体一点吗? - Boris Stitnicky
+1。不错,谢谢Boris。我喜欢Brad的正则表达式和三元运算符,但这个更快。 - djoll
7
建议:my_hash.each_pair { | k, v | my_hash[k] = Integer(v) rescue v } - undur_gongor
不仅如此,这还避免了你自己解析整数的需要。想象一下,如果你想转换为浮点数,其中字符串可以像“-3.4e-19”这样。 - Boris Stitnicky
@undur_gongor:我认为在Ruby中,我将永远对Python的块羡慕不已 :) - Boris Stitnicky
显示剩余4条评论

1

我认为你知道哪些字段应该是整数(你的消费代码可能依赖于此),所以我建议你转换特定的字段。

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

虽然我很懒,也喜欢数据驱动的逻辑,这样我就可以在其他XML上复制算法而不必担心匹配字段名称和数据类型。将字段映射到数据类型会在我的算法中硬编码模式,我想避免这种情况,并且还会引入更多的错误和笨拙操作。DNRY。 - djoll

1
我有一个类似的问题需要解决,农药分析结果以异构(糟糕的设计!)格式进入系统...负整数作为特殊代码(未检测、未测试、未定量等),nil作为未检测的同义词,浮点数用于定量化合物,字符串用于通过/失败的布尔值...等等。请稍等,这是一个在生产中运行了10年的高度修补的老应用程序,从未进行过绿地开发 ;) 两个我从顶级Ruby程序员那里学到的东西: 0) 不要迭代修改可枚举对象(返回副本) 1) 你的正则表达式不会涵盖所有情况 虽然我不是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 正是所需要的。 - djoll

0
使用正则表达式和三元运算符,你可以将这个逻辑嵌入到某个地方:
string =~ /^\d+$/ ? string.to_i : string

虽然我认为Boris的答案更加纯粹,但我对正则表达式和三元运算符有特别的偏爱。不过我猜Boris的begin/rescue可能会更快一些... - djoll
2
有些人遇到问题时,会想:“我知道,我会使用正则表达式。”现在他们有两个问题了。(Jamie Zawinski)那么"-42"呢?你的方法会将"abcd\n2\nefgh"转换为0。为什么不使用专门解决这个问题的Integer()函数呢? - undur_gongor

0

这将处理不仅整数而且所有数字。

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
}

感谢使用正则表达式判断字符串是否为有效的浮点数值提供的帮助


哎呀,注入了,一个古老的关键字 :). 你把它放在哪里了? - Boris Stitnicky
2
现在,这有点过头了 :) 除非你是按代码行数付费的 :) - Sergio Tulentsev
1
你的正则表达式在多行字符串中失效了。my_hash = {"Id"=>"1", "Name"=>"Cat", "Description"=>"Feline\n42", "Count"=>"123"} 的结果是 {:Id=>1, :Name=>"Cat", :Description=>0, :Count=>123} - undur_gongor

-1

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接