Trim a trailing .0 - ruby

I have an Excel column containing part numbers. Here is a sample
As you can see, it can be many different datatypes: Float, Int, and String. I am using roo gem to read the file. The problem is that roo interprets integer cells as Float, adding a trailing zero to them (16431 => 16431.0). I want to trim this trailing zero. I cannot use to_i because it will trim all the trailing numbers of the cells that require a decimal in them (the first row in the above example) and will cut everything after a string char in the String rows (the last row in the above example).
Currently, I have a a method that checks the last two characters of the cell and trims them if they are ".0"
def trim(row)
if row[0].to_s[-2..-1] == ".0"
row[0] = row[0].to_s[0..-3]
end
end
This works, but it feels terrible and hacky. What is the proper way of getting my Excel file contents into a Ruby data structure?

def trim num
i, f = num.to_i, num.to_f
i == f ? i : f
end
trim(2.5) # => 2.5
trim(23) # => 23
or, from string:
def convert x
Float(x)
i, f = x.to_i, x.to_f
i == f ? i : f
rescue ArgumentError
x
end
convert("fjf") # => "fjf"
convert("2.5") # => 2.5
convert("23") # => 23
convert("2.0") # => 2
convert("1.00") # => 1
convert("1.10") # => 1.1

For those using Rails, ActionView has the number_with_precision method that takes a strip_insignificant_zeros: true argument to handle this.
number_with_precision(13.00, precision: 2, strip_insignificant_zeros: true)
# => 13
number_with_precision(13.25, precision: 2, strip_insignificant_zeros: true)
# => 13.25
See the number_with_precision documentation for more information.

This should cover your needs in most cases: some_value.gsub(/(\.)0+$/, '').
It trims all trailing zeroes and a decimal point followed only by zeroes. Otherwise, it leaves the string alone.
It's also very performant, as it is entirely string-based, requiring no floating point or integer conversions, assuming your input value is already a string:
Loading development environment (Rails 3.2.19)
irb(main):001:0> '123.0'.gsub(/(\.)0+$/, '')
=> "123"
irb(main):002:0> '123.000'.gsub(/(\.)0+$/, '')
=> "123"
irb(main):003:0> '123.560'.gsub(/(\.)0+$/, '')
=> "123.560"
irb(main):004:0> '123.'.gsub(/(\.)0+$/, '')
=> "123."
irb(main):005:0> '123'.gsub(/(\.)0+$/, '')
=> "123"
irb(main):006:0> '100'.gsub(/(\.)0+$/, '')
=> "100"
irb(main):007:0> '127.0.0.1'.gsub(/(\.)0+$/, '')
=> "127.0.0.1"
irb(main):008:0> '123xzy45'.gsub(/(\.)0+$/, '')
=> "123xzy45"
irb(main):009:0> '123xzy45.0'.gsub(/(\.)0+$/, '')
=> "123xzy45"
irb(main):010:0> 'Bobby McGee'.gsub(/(\.)0+$/, '')
=> "Bobby McGee"
irb(main):011:0>

Numeric values are returned as type :float
def convert_cell(cell)
if cell.is_a?(Float)
i = cell.to_i
cell == i.to_f ? i : cell
else
cell
end
end
convert_cell("foobar") # => "foobar"
convert_cell(123) # => 123
convert_cell(123.4) # => 123.4

Related

Ruby - regex that matches string to pattern and detects unwanted occurrences [duplicate]

How do I a string against a regex such that it will return true if the whole string matches (not a substring)?
eg:
test( \ee\ , "street" ) #=> returns false
test( \ee\ , "ee" ) #=> returns true!
Thank you.
You can match the beginning of the string with \A and the end with \Z. In ruby ^ and $ match also the beginning and end of the line, respectively:
>> "a\na" =~ /^a$/
=> 0
>> "a\na" =~ /\Aa\Z/
=> nil
>> "a\na" =~ /\Aa\na\Z/
=> 0
This seems to work for me, although it does look ugly (probably a more attractive way it can be done):
!(string =~ /^ee$/).nil?
Of course everything inside // above can be any regex you want.
Example:
>> string = "street"
=> "street"
>> !(string =~ /^ee$/).nil?
=> false
>> string = "ee"
=> "ee"
>> !(string =~ /^ee$/).nil?
=> true
Note: Tested in Rails console with ruby (1.8.7) and rails (3.1.1)
So, what you are asking is how to test whether the two strings are equal, right? Just use string equality! This passes every single one of the examples that both you and Tomas cited:
'ee' == 'street' # => false
'ee' == 'ee' # => true
"a\na" == 'a' # => false
"a\na" == "a\na" # => true

how to remove backslash from a string containing an array in ruby

I have a string like this
a="[\"6000208900\",\"600020890225\",\"600900231930\"]"
#expected result [6000208900,600020890225,600900231930]
I am trying to remove the backslash from the string.
a.gsub!(/^\"|\"?$/, '')
Inside the double quoted string(""), another double quotes must be escaped by \. You can't remove it.
Use puts, you can see it is not there.
a = "[\"6000208902912790\"]"
puts a # => ["6000208902912790"]
Or use JSON
irb(main):001:0> require 'json'
=> true
irb(main):002:0> a = "[\"6000208902912790\"]"
=> "[\"6000208902912790\"]"
irb(main):003:0> b = JSON.parse a
=> ["6000208902912790"]
irb(main):004:0> b
=> ["6000208902912790"]
irb(main):005:0> b.to_s
=> "[\"6000208902912790\"]"
update (as per the last edit of OP)
irb(main):002:0> a = "[\"6000208900\",\"600020890225\",\"600900231930\"]"
=> "[\"6000208900\",\"600020890225\",\"600900231930\"]"
irb(main):006:0> a.scan(/\d+/).map(&:to_i)
=> [6000208900, 600020890225, 600900231930]
irb(main):007:0>
The code a.gsub!(/^\"|\"?$/, '') can't remove the double quote characters because they are not at the beginning and the end of the string. To get what you want try this:
a.gsub(/((?<=^\[)")|("(?=\]$))/, '')
try this:
=> a = "[\"6000208902912790\"]"
=> a.chars.select{ |x| x =~ %r|\d| }.join
=> "6000208902912790"
=> [a.chars.select { |x| x =~ %r|\d| }.join]
=> ["6000208902912790"] # <= array with string
=> [a.chars.select { |x| x =~ %r|\d| }.join].to_s
=> "[\"6000208902912790\"]" # <= come back :)
a="["6000208902912790"]" will return `unexpected tINTEGER`error;
so a="[\"6000208902912790\"]"is used with \ character for double quotes.
As a solution you should try to remove double quotes that will solve the problem.
Do this
a.gsub!(/"/, '')

Obfuscating numbers in a string?

I have a challenge that calls for obfuscating numbers in a string, such as a SSN, for example: XXX-XX-4430. I've gotten pretty close:
def hide_all_ssns(string)
string.scan(/\w{3}-\w{2}-\w{4}/)
string.gsub('/\w{3}-\w{2}', 'XXX-XX')
end
but I get an error:
Error! hide_all_ssns obfuscates any SSNs in the string expected:
"XXX-XX-1422, XXX-XX-0744, XXX-XX-8762" got: "234-60-1422,
350-80-0744, 013-60-8762" (using ==)
I initially had the regular-expression (/\d{3}-\d{2}-\d{4}/) but thought that the problem was attempting to convert the integers in the string to X. Now I'm using \w, yet I am getting the same error.
Does anyone have any insight? I'm a newbie to coding and have exhausted Ruby-doc, as well as any blogs I can find on regex/gsub, but I am getting nowhere.
You're mis-using gsub (your regular expression needs to be between forward slashes), but I still thing gsub! is what you want...
def hide_all_ssns(string)
string.scan(/\w{3}-\w{2}-\w{4}/)
string.gsub!(/\w{3}-\w{2}/, 'XXX-XX')
end
Working example:
1.9.3p448 :063 > string = "123-45-6789"
=> "123-45-6789"
1.9.3p448 :064 > def hide_all_ssns(string)
1.9.3p448 :065?> string.scan(/\w{3}-\w{2}-\w{4}/)
1.9.3p448 :066?> string.gsub!(/\w{3}-\w{2}/, 'XXX-XX')
1.9.3p448 :067?> end
=> nil
1.9.3p448 :068 > hide_all_ssns(string)
=> "XXX-XX-6789"
1.9.3p448 :069 > string
=> "XXX-XX-6789"
Why does it have to be so hard? All U.S. social security numbers are the same format, right? So, work from that point. Here's some variations on a theme, ordered by escalating obscurity:
ssn = '123-45-6789' # => "123-45-6789"
ssn[0, 6] = 'XXX-XX' # => "XXX-XX"
ssn # => "XXX-XX-6789"
Or:
numbers = ssn.scan(/\d+/) # => ["123", "45", "6789"]
'XXX-XX-' + numbers.last # => "XXX-XX-6789"
Or:
ssn = '123-45-6789' # => "123-45-6789"
ssn[0, 6] = ssn[0, 6].gsub(/\d/, 'X') # => "XXX-XX"
ssn # => "XXX-XX-6789"
Or:
ssn[0,6] = ssn[0, 6].tr('0-9', 'X') # => "XXX-XX"
ssn # => "XXX-XX-6789"
Or:
numbers = ssn.split('-') # => ["123", "45", "6789"]
[*numbers[0, 2].map{ |s| 'X' * s.size }, numbers[-1]].join('-') # => "XXX-XX-6789"
Or:
ssn[/(\d+)-(\d+)-(\d+)/] # => "123-45-6789"
[$1, $2, $3] # => ["123", "45", "6789"]
[$3, *[$2, $1].map{ |s| s.gsub(/./, 'X') }].reverse.join('-') # => "XXX-XX-6789"
Of course, using one of these would cheating, since you're supposed to figure the challenge out by yourself, but they're good food for thought and a decent starting point for your own solution.
Short and simple... You could maybe try something like this:
crypted = ('X' * 6) + "4543-2329-1354-1111".to_s[14..18]
=> "XXXXXX-1111"

How can I check a word is already all uppercase?

I want to be able to check if a word is already all uppercase. And it might also include numbers.
Example:
GO234 => yes
Go234 => no
You can compare the string with the same string but in uppercase:
'go234' == 'go234'.upcase #=> false
'GO234' == 'GO234'.upcase #=> true
a = "Go234"
a.match(/\p{Lower}/) # => #<MatchData "o">
b = "GO234"
b.match(/\p{Lower}/) # => nil
c = "123"
c.match(/\p{Lower}/) # => nil
d = "µ"
d.match(/\p{Lower}/) # => #<MatchData "µ">
So when the match result is nil, it is in uppercase already, else something is in lowercase.
Thank you #mu is too short mentioned that we should use /\p{Lower}/ instead to match non-English lower case letters.
I am using the solution by #PeterWong and it works great as long as the string you're checking against doesn't contain any special characters (as pointed out in the comments).
However if you want to use it for strings like "Überall", just add this slight modification:
utf_pattern = Regexp.new("\\p{Lower}".force_encoding("UTF-8"))
a = "Go234"
a.match(utf_pattern) # => #<MatchData "o">
b = "GO234"
b.match(utf_pattern) # => nil
b = "ÜÖ234"
b.match(utf_pattern) # => nil
b = "Über234"
b.match(utf_pattern) # => #<MatchData "b">
Have fun!
You could either compare the string and string.upcase for equality (as shown by JCorc..)
irb(main):007:0> str = "Go234"
=> "Go234"
irb(main):008:0> str == str.upcase
=> false
OR
you could call arg.upcase! and check for nil. (But this will modify the original argument, so you may have to create a copy)
irb(main):001:0> "GO234".upcase!
=> nil
irb(main):002:0> "Go234".upcase!
=> "GO234"
Update: If you want this to work for unicode.. (multi-byte), then string#upcase won't work, you'd need the unicode-util gem mentioned in this SO question

How do I test a WHOLE string against regex in ruby?

How do I a string against a regex such that it will return true if the whole string matches (not a substring)?
eg:
test( \ee\ , "street" ) #=> returns false
test( \ee\ , "ee" ) #=> returns true!
Thank you.
You can match the beginning of the string with \A and the end with \Z. In ruby ^ and $ match also the beginning and end of the line, respectively:
>> "a\na" =~ /^a$/
=> 0
>> "a\na" =~ /\Aa\Z/
=> nil
>> "a\na" =~ /\Aa\na\Z/
=> 0
This seems to work for me, although it does look ugly (probably a more attractive way it can be done):
!(string =~ /^ee$/).nil?
Of course everything inside // above can be any regex you want.
Example:
>> string = "street"
=> "street"
>> !(string =~ /^ee$/).nil?
=> false
>> string = "ee"
=> "ee"
>> !(string =~ /^ee$/).nil?
=> true
Note: Tested in Rails console with ruby (1.8.7) and rails (3.1.1)
So, what you are asking is how to test whether the two strings are equal, right? Just use string equality! This passes every single one of the examples that both you and Tomas cited:
'ee' == 'street' # => false
'ee' == 'ee' # => true
"a\na" == 'a' # => false
"a\na" == "a\na" # => true

Resources