Parse negative currencies with parenthesis - ruby-on-rails

I get the following currencies in an import process:
$22.21
($55.95)
I can do Money.parse('$22.21') to parse into a RubyMoney object.
However, how can I parse ($55.95) into a negative RubyMoney object? Do I need to parse it to a negative number first and then into RubyMoney object? It seems like a common request but I can't find anything via search for some odd reason.
If I get -$55.95, I can then run Money.parse('-$55.95') on it. Bonus points if the method can handle both positive and negative currencies.

from the page that #swapnilabnave linked to:
:negative_format - Sets the format for negative numbers (defaults to prepending an hyphen to the formatted number given by :format). Accepts the same fields than :format, except %n is here the absolute value of the number.
So you would use (%u%n) for the desired effect.

You could do a regex to see if the string includes parenthesis. If it does, remove them, and prepend a negative sign:
str.include?(")") ? str.gsub(/\(|\)/, "").insert(0, "-") : str
Examples:
2.0.0p247 :013 > str = "($55.95)"
=> "($55.95)"
2.0.0p247 :014 > str.include?(")") ? str.gsub(/\(|\)/, "").insert(0, "-") : str
=> "-$55.95"
This works with positive values as well:
2.0.0p247 :016 > str = "$55.95"
=> "$55.95"
2.0.0p247 :017 > str.include?(")") ? str.gsub(/\(|\)/, "").insert(0, "-") : str
=> "$55.95"

First you can delete the parenthesis and then parse
money = "($55.95)"
money = money.delete!('()') and "-#{money}" or money
Money.parse(money)
It's in the readme of RubyMoney sign_before_symbol
Why don't you use number_to_currency ?

Related

Rails array INCLUDE with only distinct words

I'm building a profanity search function which needs to find instances of an array of profane words in a long string of text.
One could do a simple include like:
if profane_words.any? {|word| self.name.downcase.include? word}
...
end
This results in a positive match if ANY of the array of profane words are present anywhere in the text.
However, if a word like 'hell' is considered profane, this would produce a positive match against "Hell's Angels" or "Hell's Kitchen", which is undesirable.
How can the above search be modified to only produce positive results against distinct words or phrases? For example, "Hell Angels" returns positive but "Hell's Angels" returns negative.
To be clear, this means we're searching for any instance of a profane word that is immediately preceded or followed by another character or apostrophe.
What about using a regex ?
profane_words.any? { |word| self.name.downcase.match? /#{word}(?!')/ }
Examples:
"hell's angels".match?(/hell(?!')/) # => false
"hell angel".match?(/hell(?!')/) # => true
(?!') is a negative lookup meaning it won't match if the word has a ' right after it. If you'd like to exclude other characters you can add it to the list with pipes e.g. (?!'|") won't match ' and ".
See https://www.regular-expressions.info/lookaround.html for reference.
And you could make it more performant like this:
self.name.downcase.match? /#{profane_words.join('|')}(?!')/
if profane_words.any? {|word| self.name.downcase.split(' ').include? word} ... end
You should definitely use a Regex containing all your profane words followed by a space or period. Bellow yo
> "Hell's angels".match(/(hell|shit)[ .]/i)
=> nil
> "Hell angels".match(/(hell|shit)[ .]/i)
=> #<MatchData "Hell " 1:"Hell">
> "Hell's angels shit".match(/(hell|shit)[ .]/i)
=> nil

How do i write a regular expression to capture a word after a number?

I'm using Rails 5. I'm having an issue writing a regular expression. I want to write an expression that will parse a string, looking for a positive number and then the first word that follows the number, whether or not there's a space or other type of word boundary between the number and the next word. So I tried
2.4.0 :006 > str = "2g"
=> "2g"
2.4.0 :007 > str.match(/\W?(([0-9]+(\.[0-9]+)?)\W*(^[0-9:\/]+))/)
=> nil
but as you can see no matches occur. I would expect the match to capture the "2" and then the "g". Similarly, if I have a string like
2.4.0 :008 > str = "12.2 word next"
=> "12.2 word next"
2.4.0 :009 > str.match(/\W?(([0-9]+(\.[0-9]+)?)\W*(^[0-9:\/]+))/)
=> nil
I expect the regex to capture the "12.2" and then the next word "word". But as you can see my regex isn't cutting it. How do I fix it to capture what I need?
Are you trying to select "12.2word" or "12.2 word" in "12.2 word next" ?
As you replied in comments, to have float number in group1 and word in group2:
(\d+\.\d+|\d+)[^a-zA-Z0-9]*([a-zA-Z]+)
Full match 6-15 `12.2 good`
Group 1. 6-10 `12.2`
Group 2. 11-15 `good`
My first Answer was:
(\d+(\.\d{0,})?)[^a-zA-Z0-9]*([a-zA-Z]+)
for " 12.2 good "
Full match 6-15 `12.2 good`
Group 1. 6-10 `12.2`
Group 2. 8-10 `.2`
Group 3. 11-15 `good`
Group1 is your integer part and Group2 is null or equal to .2 in this example.
This Should Work.
([0-9.]+)\s(\w+)
Input:
12.2 word
Output:
12.2
word
Ruby Code:
re = /([0-9.]+)\s(\w+)/
str = '12.2 word'
# Print the match result
str.scan(re) do |match|
puts match.to_s
end
See: https://regex101.com/r/ncVEAT/1

Ruby: Remove number from embedded string

In a Rails app I am reading a file with key/values. An index number is embedded in the key name, and I'd like to remove it, along with one of the spacing underscores.
So in the sample data below, I'd like to convert:
PRIMER_LEFT_1_END_STABILITY into PRIMER_LEFT_END_STABILITY
PRIMER_RIGHT_1_END_STABILITY into PRIMER_RIGHT_END_STABILITY
PRIMER_PAIR_1_COMPL_ANY_TH into PRIMER_PAIR_COMPL_ANY_TH
Sample Data
PRIMER_LEFT_1_END_STABILITY=7.2000
PRIMER_RIGHT_1_END_STABILITY=7.9000
PRIMER_PAIR_1_COMPL_ANY_TH=0.00
EDIT
Thanks to #tihom for the first answer. It's partially working, but I did not specify that the embedded integer can be of any value. When over 1 digit in length the regex fails:
1.9.3-p327 :003 > "PRIMER_LEFT_221_END_STABILITY".sub(/_\d/,"")
=> "PRIMER_LEFT21_END_STABILITY"
1.9.3-p327 :004 > "PRIMER_LEFT_21_END_STABILITY".sub(/_\d/,"")
=> "PRIMER_LEFT1_END_STABILITY"
To remove the first occurrence use sub else to remove all occurrences use gsub
"PRIMER_LEFT_1_END_STABILITY".sub(/_(\d)+/,"") # => "PRIMER_LEFT_END_STABILITY"
"+" matches one or more of the preceding character. So in this case it matches one or more of any digit followed by a "_"
You can use String#tr and String#squeezeas below :
ar=['PRIMER_LEFT_1_END_STABILITY','PRIMER_RIGHT_1_END_STABILITY','PRIMER_PAIR_1_COMPL_ANY_TH']
p ar.map{|s| s.tr('0-9','').squeeze("_")}
# => ["PRIMER_LEFT_END_STABILITY", "PRIMER_RIGHT_-END_STABILITY", "PRIMER_PAIR_COMPL_ANY_TH"]
ar=["PRIMER_LEFT_221_END_STABILITY","PRIMER_LEFT_21_END_STABILITY"]
p ar.map{|s| s.tr('0-9','').squeeze("_")}
# => ["PRIMER_LEFT_END_STABILITY", "PRIMER_LEFT_END_STABILITY"]

Using Hash functions to remove duplicate content/text

I have a website with a lot of content and I am working on removing duplicates. For this I need to compare two strings and check their match percentage. I am using the ruby simhash gem: https://github.com/bookmate/simhash
The gem takes a string and returns an integer hash. I am not sure how to compare the two hashes.
X = 'King Gillette'.simhash(:split_by => //)
y = 'King Camp Gillette'.simhash(:split_by => //)
X >> 13716569836
y >> 13809628900
Can I take the difference and then percentage? Does that indicate the difference between the strings?
If you want to remove the duplicates of those strings way
or you want difference between the strings If I am getting right then simply you can do this....
>>a1='King Gillette'.split(" ")
>>=> ["King", "Gillette"]
>>a2='King Camp Gillette'.split(" ")
>>=> ["King", "Camp", "Gillette"]
>> a2-a1
>>["Camp"]

rails convert string to number

I am wondering what is a convenient function in Rails to convert a string with a negative sign into a number. e.g. -1005.32
When I use the .to_f method, the number becomes 1005 with the negative sign and decimal part being ignored.
.to_f is the right way.
Example:
irb(main):001:0> "-10".to_f
=> -10.0
irb(main):002:0> "-10.33".to_f
=> -10.33
Maybe your string does not include a regular "-" (dash)? Or is there a space between the dash and the first numeral?
Added:
If you know that your input string is a string version of a floating number, eg, "10.2", then .to_f is the best/simplest way to do the conversion.
If you're not sure of the string's content, then using .to_f will give 0 in the case where you don't have any numbers in the string. It will give various other values depending on your input string too. Eg
irb(main):001:0> "".to_f
=> 0.0
irb(main):002:0> "hi!".to_f
=> 0.0
irb(main):003:0> "4 you!".to_f
=> 4.0
The above .to_f behavior may be just what you want, it depends on your problem case.
Depending on what you want to do in various error cases, you can use Kernel::Float as Mark Rushakoff suggests, since it raises an error when it is not perfectly happy with converting the input string.
You should be using Kernel::Float to convert the number; on invalid input, this will raise an error instead of just "trying" to convert it.
>> "10.5".to_f
=> 10.5
>> "asdf".to_f # do you *really* want a zero for this?
=> 0.0
>> Float("asdf")
ArgumentError: invalid value for Float(): "asdf"
from (irb):11:in `Float'
from (irb):11
>> Float("10.5")
=> 10.5

Resources