How to convert only strings hash values that are numbers to integers

强颜欢笑 提交于 2020-02-27 09:42:30

问题


I have rows of hashes imported from several different XML database dumps that look like this (but with varying keys):

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

I tried using #to_i but it converts a non-number string to 0:

"Feline".to_i
# => 0

But what I'd like is a way for "Feline" to remain a string, while Id and Count in the above example become integers 1 and 123.

Is there an easy way to convert only the strings values that are numbers into integers?


回答1:


use 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 ] } ]

ADDED LATER: With my y_support gem, you can make hash operations even more concise.

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

YSupport can be installed by gem install y_support and also offers Hash#with_keys, Hash#with_values!, Hash#with_keys! that do what you expect they do, and Hash#modify that expects a binary block returning a pair of values, modifying the hash in place. There have been proposals to add such methods directly to the Ruby core in the future.




回答2:


One line answer: Using regex approach

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

Using Integer approach

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



回答3:


I think you know what fields should be integers (your consuming code probably depends on it), so I would recommend you convert the specific fields.

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



回答4:


I had a similar problem to solve, where results for pesticide analysis came into the system as a heterogeneous (bad design!) format... negative integers as special codes (not detected, not tested, not quantified etc...), nil as synonym as not detected, floats for quantified compounds and strings for pass/fail boolean... Hold your horses, this is a 10 years old-running in production, never greenfield-ed highly patched app ;)

Two things that I learned from top rubists:

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

1) YOUR REGEX WON'T COVER ALL CASES

While I am not a big fan of rescue, I think it fits the purpose of keeping the code clean. So, I've been using this to mitigate my input:

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

I would even

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



回答5:


Using a regex and the ternary operator, you could incorporate this into the logic somewhere:

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



回答6:


This will handle not only integers but all numbers.

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
}

Thanks to Determine if a string is a valid float value for regexp




回答7:


Define a new method for String: String#to_number

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

Test it:

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


来源:https://stackoverflow.com/questions/12318021/how-to-convert-only-strings-hash-values-that-are-numbers-to-integers

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!