Reputation:
I'm using Ruby 2.4. I have an array of strings that are supposed to be integers ...
["1", "55", "25", ... ]
I have the following for figuring out the minimum number in the array
data.map(&:to_i).min
This works great, except for the times when a non-integer string creeps into my array, like
["1", "a", "2", "3"]
With my data.map(&:to_i).min
line, the above returns zero. How do I modify my code so that it will only take the min of elements in the array that are actually integers?
Upvotes: 2
Views: 477
Reputation: 110675
When I want to convert a string to an integer, if and only it is a string representation of an integer, I always use Kernel::Integer:
arr = ["3", "a", "2", "0x01", "0c", "0b11", "0o03", "4", "1_000"]
arr.select { |s| Integer(s) rescue nil }.min_by { |s| Integer(s) }
#=> "0x01"
Here
arr.select { |s| Integer(s) rescue nil }
#=> ["3", "2", "0x01", "0b11", "0o03", "4", "1_000"]
Note "0x01".to_i #=> 0
(not!), which is why Integer
must be used twice.
If the string is not a representation of an integer, Integer
raises an exception:
Integer("a")
#=> ArgumentError: invalid value for Integer(): "a"
which is rescued in-line, causing nil
to be returned, resulting is the string not being selected. (One could of course go the extra mile and use a clause that only rescues ArgumentError
exceptions.)
The question asks for an element of the array (a string) to be returned. If its integer equivalent is desired, you can write:
a = arr.map { |s| Integer(s) rescue nil }.compact.min
#=> 1
where
arr.map { |s| Integer(s) rescue nil }
#=> [3, nil, 2, 1, nil, 3, 3, 4, 1_000]
Note
['a', 'b'].map { |s| Integer(s) rescue nil }.compact.min
#=> nil
Upvotes: 3
Reputation: 9497
Using Enumerable#grep_v we can do the following and exclude wordy-strings:
arr = ["1", "a", "2", "3"]
arr.grep_v(/[a-zA-Z]+/).min_by(&:to_i) #=> "1"
Modify the regexp as you feel appropriate.
Upvotes: 0
Reputation: 211560
You have a few ways of doing this, but the easiest is to screen out non-numerical values if you want to ignore the garbage:
data = ["1", "a", "2", "3"]
data.grep(/\A\-?\d+\z/).map(&:to_i).min
# => 1
The grep
method makes it pretty easy to pick out things that match a particular pattern, as well as many other things.
Upvotes: 4