Converting numeric string to numeric in Ruby - ruby

I want a method like to_numeric(str) which convert numeric string 'str' into its numeric form else return nil. By numeric form if string is in integer method should return integer and it string is in float it should return float.
I have tried with following code. It works fine but need better solution if possible.
def to_numeric(str)
Integer(str)
rescue
Float(str) if Float(str) rescue nil
end
One important thing I forgot to mention is "I don't know the type of my input".
My use case:
arr = [1, 1.5, 2, 2.5, 4]
some_input = get_input_from_some_source
if arr.include?(to_numeric(some_input))
# do something
end

You can use BigDecimal#frac to achieve what you want
require 'bigdecimal'
def to_numeric(anything)
num = BigDecimal.new(anything.to_s)
if num.frac == 0
num.to_i
else
num.to_f
end
end
It can handle
#floats
to_numeric(2.3) #=> 2.3
#rationals
to_numeric(0.2E-4) #=> 2.0e-05
#integers
to_numeric(1) #=> 1
#big decimals
to_numeric(BigDecimal.new("2"))
And floats, rationals and integers in form of strings, too

Convert it to Float using String#to_f method. Since ruby using duck typing you may not care if it can be an Integer.
If it looks like a numeric, swims like a numeric and quacks like a numeric, then it probably is a numeric.
But be aware! to_f does not throw any exceptions:
"foobar".to_f # => 0

If you really insist to differentiate between Integer and Floats, then you can implement to_numeric like this:
def to_numeric(thing)
return thing.to_s.to_i if thing.to_s == thing.to_s.to_i.to_s
return thing.to_s.to_f if thing.to_s == thing.to_s.to_f.to_s
thing
end
It converts an object to an integer, if its string representation looks like an integer (same with float), or returns the unchanged thing if not:
['1', '1.5', 'foo', :bar, '2', '2.5', File].map {|obj| to_numeric obj}
# => [1, 1.5, "foo", :bar, 2, 2.5, File]

Here are some options:
Use floats for comparison:
arr = [1, 1.5, 2, 2.5, 4]
arr.include? "4.0".to_f #=> true
Use strings for comparison:
arr = %w(1 1.5 2 2.5 4)
arr.include? "4" #=> true
Use eval for conversion:
eval("4.0") #=> 4.0
eval("4") #=> 4
But you have to be very careful when using eval, see #tessi's comment.

def to_numeric(str)
str.include?(".") ? str.to_f : str.to_i
end

Related

How use dig method several for children object

I have an hash
{:result=>
{:"1"=>
[{:"1"=>1,
:"2"=>"4698192612070913717",
:"5"=>
{:"1"=>{:"1"=>"1.0.0"},
:"2"=>
{:"1"=>1,
:"2"=>"1525341956127",
:"3"=>1000000000,
:"4"=>0,
:"5"=>{:"1"=>1000000000}},
:"3"=>["17"],
:"6"=>"4704522736971289334",
:"8"=>["4618851880555471022"],
:"9"=>[1]},
:"6"=>{:"3"=>{:"2"=>false}, :"4"=>{:"1"=>false}},
:"7"=>{:"1"=>1},
:"8"=>"production"},
{:"1"=>4,
:"2"=>"4700283765268993541",
:"6"=>{:"2"=>{:"1"=>200}, :"3"=>{:"2"=>false}, :"4"=>{:"1"=>false}},
:"8"=>"beta"},
{:"1"=>5,
:"2"=>"4699074054925986704",
:"6"=>{:"2"=>{:"1"=>100}, :"3"=>{:"2"=>false}, :"4"=>{:"1"=>false}},
:"8"=>"alpha"},
{:"1"=>10,
:"2"=>"4697702456121346981",
:"6"=>{:"2"=>{:"1"=>50}, :"3"=>{:"2"=>false}, :"4"=>{:"1"=>false}},
:"8"=>"internal"}],
:"3"=>{:"1"=>true, :"2"=>{:"1"=>{:"1"=>false}, :"2"=>{:"1"=>false}}},
:"4"=>false},
:xsrf=>"AMtNNDFJl06mR54j2zxFjYIYfGQR22sUKA:1528830206790"}
I am looking for simple way to return a value or nil
I have tried this
result[:'result'][:'1'][1].dig(:'5').dig(:'1').dig(:'1')
but it's not working
What can I do to avoid this
if result[:'result'][:'1'][1].dig(:'5')
puts result[:'result'][:'1'][1][:'5'][:'1'][:'1']
end
The idea behind dig is that you can go several levels deep into a hash at the same time and return nil if the key doesn't exist at any level during the 'digging'. So result[:'result'][:'1'][1].dig(:'5', :'1', :'1') will do what you are looking for and clean up your code as well. In fact, you could make it a little safer if you wanted by doing result.dig(:result, :'1', 1, :'5', :'1', :'1')
dig is not a single method, but a family of four methods, all of which made their debut in Ruby v2.3: Array#dig, Hash#dig, Struct#dig and OpenStruct#dig.
For example,
h = { a: [1, { c: 2, d: 3 }], b: 2 }
h.dig(:a, 1, :d)
#=> 3
employs Hash#dig because dig's receiver is a hash. Moreover, one might expect that when, in an intermediate calculation, dig has unearthed [1, { c: 2, d: 3 }] it will pass the shovel to Array#dig for further excavation.
Suppose
h = { a: [1, 2] }
Then
h.dig(:a, 1) #=> 2
h.dig(:a).dig(1) #=> 2
Does that mean the two are equivalent? Try this:
h.dig('cat', 1) #=> nil
h.dig('cat').dig(1) #=> NoMethodError: undefined method `dig' for nil:NilClass
The exception is due to the fact that h.dig('cat') #=> nil and NilClass has no instance method dig, so nil.dig(1) raises the exception. No, the two expressions are not equivalent.
If the value of the variable result is the OPs hash, we have (as pointed out by #Isaiah) the following.
result.dig(:result, :'1', 0, :"5", :"1", :"1")
#=> "1.0.0"
result.dig(:result, :'1', 0, :cat, :"1", :"1")
#=> nil
Note that dig will still raise an exception if the wrong data type is used:
[1, 2].dig(:a)
#=> TypeError: no implicit conversion of Symbol into Integer
To support versions of Ruby prior to 2.3 (where dig is not available) we can write the following, using Enumerable#reduce (aka inject).
arr = [:result, :'1', 0, :"5", :"1", :"1"]
arr.reduce(result) { |memo, obj| memo && memo[obj] }
#=> "1.0.0"
arr = [:result, :'1', 0, :cat, :"1", :"1"]
arr.reduce(result) { |memo, obj| memo && memo[obj] }
#=> nil

How can either convert a string into a hash or add it into a hash

So in basic Ruby I am trying to figure out how to either convert a string into a hash or put a string into a hash. I want the pokemon item as the key and a integer for the value.
Something like this:
hash = {}
pokemon_list = "pikachu charizard jigglypuff bulbasaur"
def create_poke_list(string)
hash << string.split
end
create_poke_list
Expected output:
hash
#=> {"pikachu"=>0, "charizard"=>0, "jigglypuff"=>0, "bulbasaur"=>0}
pokemon_list.split.product([0]).to_h
#=> {"pikachu"=>0, "charizard"=>0, "jigglypuff"=>0, "bulbasaur"=>0}
The steps:
a = pokemon_list.split
#=> ["pikachu", "charizard", "jigglypuff", "bulbasaur"]
b = a.product([0])
#=> [["pikachu", 0], ["charizard", 0], ["jigglypuff", 0], ["bulbasaur", 0]]
b.to_h
#=> <hash shown above>
Alternatively,
Hash[pokemon_list.split.product([0])]
Here Array#product is just a short-hand form of pokeman_list.zip(a) where a is an array consisting of pokenman_list.size equal elements, here zero. See Enumerable#zip also.
Or use String#gsub!
This is another way that does not require the string to be converted to an array.
pokemon_list.gsub(/[[:alpha:]]+/).with_object({}) { |w,h| h[w] = 0 }
#=> {"pikachu"=>0, "charizard"=>0, "jigglypuff"=>0, "bulbasaur"=>0}
This works because gsub returns an enumerator when executed without a block. It's admittedly an unusual use of that method (since it does not replace in characters in the string), but one that I've found useful at times.
If you need a hash that is initialised with default value of 0, you could simply do
hash = Hash.new(0)
p hash["pikachu"]
#=> 0

Setting hash value (fractions) in ruby and sorting

I receive the following hash:
my_hash = {a:(1/20), b:(1/26)}
But when I see the hash I get the following:
irb(main):019:0> my_hash = {a:(1/20), b:(1/26)}
=> {:a=>0, :b=>0}
irb(main):020:0> my_hash
=> {:a=>0, :b=>0}
As you can see it convert to Integer (0)
How can I leave as Rational, or float so I can sort my_hash.sort_by {|key, value| value}?
The syntax for a Rational literal in Ruby is <numerator>/<denominator>r, e.g. 1/2r or 23/42r. What you have is just integer division: 1 divided by 20 is 0.
my_hash = { a: 1/20r, b: 1/26r }
#=> { :a => (1/20), :b => (1/26) }
It looks like you might be a Smalltalk or Scheme programmer, but in those languages the situation is different: they had rational literals from the beginning, Ruby only got them later, and so it needs an explicit annotation (the r suffix) to tell rational literals apart from just integer division; otherwise you would break existing programs.
Define as such:
my_hash = {a:(1.0/20.0), b:(1.0/26.0)}
Or alternatively:
my_hash = {a:(1.to_f/20.to_f), b:(1.to_f/26.to_f)}

How to use &proc argument inside method

Array#max_by returns only a single value, but I want to have all values that have the max value.
hashes = [{a: 1, b:2}, {a:2, b:3}, {a:1, b:3}]
max = hashes.map{|h| h[:b]}.max
hashes.select{|h| h[:b] == max}
# => [{a: 2, b: 3}, {a: 1, b: 3}]
This code works fine, and I want to add it to Array class.
class Array
def max_values_by(&proc)
max = map(&proc).max
# I don't know how to use `select` here.
end
end
How to access the value of the &proc argument?
Use the proc in the block passed to select by calling it with call:
class Array
def max_values_by(&proc)
max = map(&proc).max
select { |h| proc.call(h) == max }
end
end
hashes.max_values_by { |h| h[:b] }
=> [{a: 2, b: 3}, {a: 1, b: 3}]
or with yield, which gives identical results:
def max_values_by(&proc)
max = map(&proc).max
select { |h| yield(h) == max }
end
Although proc.call is a little longer than yield, I prefer it in this case because it makes it clearer that the same block is being used in two places in the method, and because it's weird to use both the implicit block passing of yield and the explicit passing of &proc in the same method.
#DaveSchweisguth suggests a great implementation using select, like you requested. Another way of achieving the same result is by using group_by, like this:
>> hashes.group_by{|h| h[:b]}.max.last
=> [{:a=>2, :b=>3}, {:a=>1, :b=>3}]
or monkey-patched into Array as:
class Array
def max_values_by(&proc)
group_by(&proc).max.last
end
end

simple hash merge by array of keys and values in ruby (with perl example)

In Perl to perform a hash update based on arrays of keys and values I can do something like:
#hash{'key1','key2','key3'} = ('val1','val2','val3');
In Ruby I could do something similar in a more complicated way:
hash.merge!(Hash[ *[['key1','key2','key3'],['val1','val2','val3']].transpose ])
OK but I doubt the effectivity of such procedure.
Now I would like to do a more complex assignment in a single line.
Perl example:
(#hash{'key1','key2','key3'}, $key4) = &some_function();
I have no idea if such a thing is possible in some simple Ruby way. Any hints?
For the Perl impaired, #hash{'key1','key2','key3'} = ('a', 'b', 'c') is a hash slice and is a shorthand for something like this:
$hash{'key1'} = 'a';
$hash{'key2'} = 'b';
$hash{'key3'} = 'c';
In Ruby 1.9 Hash.[] can take as its argument an array of two-valued arrays (in addition to the old behavior of a flat list of alternative key/value arguments). So it's relatively simple to do:
mash.merge!( Hash[ keys.zip(values) ] )
I do not know perl, so I'm not sure what your final "more complex assignment" is trying to do. Can you explain in words—or with the sample input and output—what you are trying to achieve?
Edit: based on the discussion in #fl00r's answer, you can do this:
def f(n)
# return n arguments
(1..n).to_a
end
h = {}
keys = [:a,:b,:c]
*vals, last = f(4)
h.merge!( Hash[ keys.zip(vals) ] )
p vals, last, h
#=> [1, 2, 3]
#=> 4
#=> {:a=>1, :b=>2, :c=>3}
The code *a, b = some_array will assign the last element to b and create a as an array of the other values. This syntax requires Ruby 1.9. If you require 1.8 compatibility, you can do:
vals = f(4)
last = vals.pop
h.merge!( Hash[ *keys.zip(vals).flatten ] )
You could redefine []= to support this:
class Hash
def []=(*args)
*keys, vals = args # if this doesn't work in your version of ruby, use "keys, vals = args[0...-1], args.last"
merge! Hash[keys.zip(vals.respond_to?(:each) ? vals : [vals])]
end
end
Now use
myhash[:key1, :key2, :key3] = :val1, :val2, :val3
# or
myhash[:key1, :key2, :key3] = some_method_returning_three_values
# or even
*myhash[:key1, :key2, :key3], local_var = some_method_returning_four_values
you can do this
def some_method
# some code that return this:
[{:key1 => 1, :key2 => 2, :key3 => 3}, 145]
end
hash, key = some_method
puts hash
#=> {:key1 => 1, :key2 => 2, :key3 => 3}
puts key
#=> 145
UPD
In Ruby you can do "parallel assignment", but you can't use hashes like you do in Perl (hash{:a, :b, :c)). But you can try this:
hash[:key1], hash[:key2], hash[:key3], key4 = some_method
where some_method returns an Array with 4 elements.

Resources