regex to pull in number with decimal or comma - ruby

This is my line of code:
col_value = line_item[column].scan(/\d+./).join().to_i
When I enter 30,000 into the textfield, col_value is 30.
I want it to bring in any number:
30,000
30.5
30.55
30000
Any of these are valid...
Is there a problem with the scan and or join which would cause it to return 30? Using the suggested regexes below still retunrs 30 e.g.
col_value = line_item[column].scan(/\d+[,.]?\d+/).join().to_i
Could it be that "to_i" converts "30,000" to 30??

This regex will match you desired output:
\d+[,.]?\d*
here ? is used as optional to match.
DEMO

\d+(?:[,.]\d+)?
Try this.This should do it for you.

Yes, "30,000".to_i #=> 30". See String#to_i: "Extraneous characters past the end of a valid number are ignored."
I suggest you first remove the commas, then apply a regex:
R = /
\d+ # match >= 0 digits
| # or
\d+\.\d+ # match > 0 digits, a decimal point, then > 0 digits
/x # extended mode
str = "30,000 30.5 30.55 30000 1. .1"
str1 = str.tr(',','')
#=> "30000 30.5 30.55 30000 1. .1"
a = str1.scan(R)
#=> ["30000", "30", "5", "30", "55", "30000"]
a.map(&:to_i)
#=> [30000, 30, 5, 30, 55, 30000]
After chaining, we have:
str.tr(',','').scan(R).map(&:to_i)
If the desired solution is instead:
#=> [30000, 30, 5, 30, 55, 30000, 1, 0]
the regex needs to be modified as follows:
R = /
\d+ # match >= 0 digits
| # or
\d+\.\d+ # match > 0 digits, a decimal point, then > 0 digits
| # or
\d+\. # match > 0 digits, then a decimal point
| # or
\.\d+ # match a decimal point, then > 0 digits
/x # extended mode

Related

Prime Digit Sums

So I'm doing one of those programming challenges on HackerRank to help build my skills. (No this is NOT for an interview! The problem I am on is the Prime Digit Sum. (Full description: https://www.hackerrank.com/challenges/prime-digit-sums/problem) Basically given a value n, I am to find all numbers that are n digits long that meet the following three criteria:
Every 3 consecutive digits sums to a prime number
Every 4 consecutive digits sums to a prime number
Every 5 consecutive digits sums to a prime number
See the link for a detailed breakdown...
I've got a basic function that works, problem is that when n gets big enough it breaks:
#!/bin/ruby
require 'prime'
def isChloePrime?(num)
num = num.to_s
num.chars.each_cons(5) do |set|
return false unless Prime.prime?(set.inject(0) {|sum, i| sum + i.to_i})
end
num.chars.each_cons(4) do |set|
return false unless Prime.prime?(set.inject(0) {|sum, i| sum + i.to_i})
end
num.chars.each_cons(3) do |set|
return false unless Prime.prime?(set.inject(0) {|sum, i| sum + i.to_i})
end
return true
end
def primeDigitSums(n)
total = 0
(10**(n-1)..(10**n-1)).each do |i|
total += 1 if isChloePrime?(i)
end
return total
end
puts primeDigitSums(6) # prints 95 as expected
puts primeDigitSums(177779) # runtime error
If anyone could point me in the right direction that would be awesome. Not necessarily looking for a "here's the answer". Ideally would love a "try looking into using this function...".
UPDATE here is version 2:
#!/bin/ruby
require 'prime'
#primes = {}
def isChloePrime?(num)
num = num.to_s
(0..num.length-5).each do |i|
return false unless #primes[num[i,5]]
end
return true
end
def primeDigitSums(n)
total = 0
(10**(n-1)...(10**n)).each do |i|
total += 1 if isChloePrime?(i)
end
return total
end
(0..99999).each do |val|
#primes[val.to_s.rjust(5, "0")] = true if [3,4,5].all? { |n| val.digits.each_cons(n).all? { |set| Prime.prime? set.sum } }
end
I regard every non-negative integer to be valid if the sum of every sequence of 3, 4 and 5 of its digits form a prime number.
Construct set of relevant prime numbers
We will need to determine if the sums of digits of 3-, 4- and 5-digit numbers are prime. The largest number will therefore be no larger than 5 * 9. It is convenient to construct a set of those primes (a set rather than an array to speed lookups).
require 'prime'
require 'set'
primes = Prime.each(5*9).to_set
#=> #<Set: {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43}>
Construct transition hash
valid1 is a hash whose keys are all 1-digit numbers (all of which are valid). The value of the key 0 is an array of all 1-digit numbers. For 1-9 the values are arrays of 2-digit numbers (all of which are valid) that are obtained by appending a digit to the key. Collectively, the values include all 2-digit numbers.
valid1 = (0..9).each_with_object({}) { |v1,h|
h[v1] = 10.times.map { |i| 10 * v1 + i } }
valid2 is a hash that maps 2-digit numbers (all valid) to arrays of valid 3-digit numbers that are obtained by appending a digit to the 2-digit number. Collectively, the values include all valid 3-digit numbers. All values are non-empty arrays.
valid2 = (10..99).each_with_object({}) do |v2,h|
p = 10 * v2
b, a = v2.digits
h[v2] = (0..9).each_with_object([]) { |c,arr|
arr << (p+c) if primes.include?(a+b+c) }
end
Note that Integer#digits returns an array with the 1's digit first.
valid3 is a hash that maps valid 3-digit numbers to arrays of valid 4-digit numbers that are obtained by appending a digit to the key. Collectively, the values include all valid 4-digit numbers. 152 of the 303 values are empty arrays.
valid3 = valid2.values.flatten.each_with_object({}) do |v3,h|
p = 10 * v3
c, b, a = v3.digits
h[v3] = (0..9).each_with_object([]) do |d,arr|
t = b+c+d
arr << (p+d) if primes.include?(t) && primes.include?(t+a)
end
end
valid4 is a hash that maps valid 4-digit numbers to arrays of valid 4-digit numbers that are obtained by appending a digit to the key and dropping the first digit of key. valid5.values.flatten.size #=> 218 is the number of valid 5-digit numbers. 142 of the 280 values are empty arrays.
valid4 = valid3.values.flatten.each_with_object({}) do |v4,h|
p = 10 * v4
d, c, b, a = v4.digits
h[v4] = (0..9).each_with_object([]) do |e,arr|
t = c+d+e
arr << ((p+e) % 10_000) if primes.include?(t) &&
primes.include?(t += b) && primes.include?(t + a)
end
end
We merge these four hashes to form a single hash #transition. The former hashes are no longer needed. #transition has 294 keys.
#transition = [valid1, valid2, valid3, valid4].reduce(:merge)
#=> {0=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
# 1=>[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
# ...
# 9=>[90, 91, 92, 93, 94, 95, 96, 97, 98, 99],
# 10=>[101, 102, 104, 106], 11=>[110, 111, 113, 115, 119],
# ...
# 97=>[971, 973, 977], 98=>[980, 982, 986], 99=>[991, 995],
# 101=>[1011], 102=>[1020], 104=>[], 106=>[], 110=>[1101],
# ...
# 902=>[9020], 904=>[], 908=>[], 911=>[9110], 913=>[], 917=>[],
# 1011=>[110], 1020=>[200], 1101=>[], 1110=>[], 1200=>[],
# ...
# 8968=>[], 9020=>[200], 9110=>[], 9200=>[]}
Transition method
This is the method that will be used to update counts each time n, the number of digits, is incremented by one.
def next_counts(counts)
counts.each_with_object({}) do |(k,v),new_valid|
#transition[k].each do |new_v|
(new_valid[new_v] = new_valid[new_v].to_i + v) if #transition.key?(k)
end
end
end
prime_digit_sum method
def prime_digit_sum(n)
case n
when 1 then 10
when 2 then 90
when 3 then #transition.sum { |k,v| (10..99).cover?(k) ? v.size : 0 }
else
counts = #transition.select { |k,_| (100..999).cover?(k) }.
values.flatten.product([1]).to_h
(n - 4).times { counts = next_counts(counts) }
counts.values.sum % (10**9 + 7)
end
end
Note that, for n = 4 the hash counts has keys that are valid 4-digit numbers and values that all equal 1:
counts = #transition.select { |k,_| (100..999).cover?(k) }.
values.flatten.product([1]).to_h
#=> {1011=>1, 1020=>1, 1101=>1, 1110=>1, 1200=>1, 2003=>1, 2005=>1,
# ...
# 8902=>1, 8920=>1, 8968=>1, 9020=>1, 9110=>1, 9200=>1}
counts.size
#=> 280
As shown, for n >= 5, counts is updated each time n is incremented by one. The sum of the values equals the number of valid n-digit numbers.
The number formed by the last four digits of every valid n-digit numbers is one of count's keys. The value of each key is an array of numbers that comprise the last four digits of all valid (n+1)-digit numbers that are produced by appending a digit to the key.
Consider, for example, the value of counts for n = 6, which is found to be the following.
counts
#=> {1101=>1, 2003=>4, 2005=>4, 300=>1, 302=>1, 304=>1, 308=>1, 320=>1,
# 322=>1, 326=>1, 328=>1, 380=>1, 382=>1, 386=>1, 388=>1, 500=>1,
# 502=>1, 506=>1, 508=>1, 560=>1, 562=>1, 566=>1, 568=>1, 1200=>7,
# 3002=>9, 3020=>4, 3200=>6, 5002=>6, 9200=>4, 200=>9, 1020=>3, 20=>3,
# 5200=>4, 201=>2, 203=>2, 205=>2, 209=>2, 5020=>2, 9020=>1}
Consider the key 2005 and note that
#transition[2005]
#=> [50, 56]
We see that there are 4 valid 6-digit numbers whose last four digits are 2005 and that, for each of those 4 numbers, a valid number is produced by adding the digits 0 and 6, resulting in numbers whose last 5-digits are 20050 and 20056. However, we need only keep the last four digits, 0050 and 0056, which are the numbers 50 and 56. Therefore, when recomputing counts for n = 7--call it counts7--we add 4 to both counts7[50] and counts7[56]. Other keys k of counts (for n=6) may be such that #transition[k] have values that include 50 and 56, so they too would contribute to counts7[50] and counts7[50].
Selective results
Let's try it for various values of n
puts "digits nbr valid* seconds"
[1, 2, 3, 4, 5, 6, 20, 50, 100, 1_000, 10_000, 40_000].each do |n|
print "%6d" % n
t = Time.now
print "%11d" % prime_digit_sum(n)
puts "%10f" % (Time.now-t).round(4)
end
puts "\n* modulo (10^9+7)"
digits nbr valid* seconds
1 10 0.000000
2 90 0.000000
3 303 0.000200
4 280 0.002200
5 218 0.000400
6 95 0.000400
20 18044 0.000800
50 215420656 0.001400
100 518502061 0.002700
1000 853799949 0.046100
10000 590948890 0.474200
40000 776929051 2.531600
I would approach the problem by pre-calculating a list of all the allowed 5-digit sub-sequences: '00002' fails while '28300' is allowed etc. This could perhaps be set up as a binary array or hash set.
Once you have the list, then you can check any number by moving a 5-digit frame over the number one step at a time.

Retain 0's when incrementing number

If I increment integer 003 by 1, I get 4.
num = 003
num += 1
# => 4
I want it to be 004. How can I retain the 0's in this?
You can convert an integer to a String and give it a fixed amount of padding using String#rjust, where the first argument is the total width of the resulting String, and the second argument is the character to use for padding:
>> int = 3
>> str = int.to_s.rjust(3, '0')
#=> "003"
And then you can increment that string using String#next:
>> str.next
#=> "004"
First of all, the leading zero in 003 makes that an octal integer literal in Ruby, not a decimal. Of course, it doesn't matter with 3 but it does matter for 009 (which is a SyntaxError), 030, etc. Presumably you really mean to say:
num = 3
as the leading zeroes aren't really part of the number, they're just formatting that you want on output. To format the number, you'd use String#% or sprintf:
> '%03d' % 3
=> "003"
> sprintf('%03d', 3)
=> "003"

Why does Ruby return for `str[-1..1]` what it does?

Suppose we have a string str. If str contains only one character, for example, str = "1", then str[-1..1] returns 1.
But if the size (length) of str is longer than one, like str = "anything else", then str[-1..1] returns "" (empty string).
Why does Ruby interpret string slicing like this?
This behaviour is just how ranges of characters work.
The range start is -1, which is the last character in the string. The range end is 1, which is the second position from the start.
So for a one character string, this is equivalent to 0..1, which is that single character.
For a two character string, this is 1..1, which is the second character.
For a three character string, this is 2..1, which is an empty string. And so on for longer strings.
To get a non-trivial substring, the start position has to represent a position earlier than the end position.
For a single-length string, index -1 is the same as index 0, which is smaller than 1. Thus, [-1..1] gives a non-trivial substring.
For a string longer than a single character, index -1 is larger than index 0. Thus, [-1..1] cannot give a non-trivial substring, and by default, it returns an empty string.
Writing down the indices usually helps me:
# 0 1 2 3 4 5 6 7 8 9 10 11 12
str = 'a' 'n' 'y' 't' 'h' 'i' 'n' 'g' ' ' 'e' 'l' 's' 'e' #=> "anything else"
# -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1
You can refer to each character by either its positive or negative index. For example, you can use either 3 or -10 to refer to "t":
str[3] #=> "t"
str[-10] #=> "t"
and either 7 or -6 to refer to "g":
str[7] #=> "g"
str[-6] #=> "g"
Likewise, you can use each of these indices to retrieve "thing" via a range:
str[3..7] #=> "thing"
str[3..-6] #=> "thing"
str[-10..7] #=> "thing"
str[-10..-6] #=> "thing"
str[-1..1] however would return an empty string, because -1 refers to the last character and 1 refers to the second. It would be equivalent to str[12..1].
But if the string consists of a single character, that range becomes valid:
# 0
str = '1'
# -1
str[-1..1] #=> "1"
In fact, 1 refers to an index after the first character, so 0 would be enough:
str[-1..0] #=> "1"

summing up string representation of year and month durations in ruby

I was wondering if there is a way to sum up multiple durations in string representations like 2 years 2 months, 10 months, 3 years and output 6 years
You could do that as follows.
str = "2 years 4 months, 10 months, 3 years, 1 month"
r = /
(\d+) # match one or more digits in capture group 1
\s+ # match one or more whitespace chars
(year|month) # match 'year' or 'month' in capture group 2
s? # optionally match 's'
\b # match a word break
/x # free-spacing regex definition mode
a = str.scan r
#=> [["2", "year"], ["4", "month"], ["10", "month"], ["3", "year"], ["1", "month"]]
h = a.each_with_object(Hash.new(0)) { |(n,period),h| h[period] += n.to_i }
#=> {"year"=>5, "month"=>15}
y, m = h["month"].divmod(12)
#=> [1, 3]
h["year"] += y
#=> 6
h["month"] = m
#=> 3
h #=> {"year"=>6, "month"=>3}
Notes:
As noted in the doc for String#scan, "If the pattern contains groups, each individual result is itself an array containing one entry per group."
Hash.new(0) creates an empty hash with a default value of zero, meaning that if that hash h does not have a key k, h[k] returns zero. Thos is sometimes called a counting hash. See the doc for Hash::new.
Numeric#divmod is a useful and greatly-underused method.

how to insert char at multiple position from end of the string

input like this:
100000000
1000
100
100000
10000
i need to start inserting char (,) from end of the string.first time after three char from end and then repeat the insert (,) after every 2 char
output
10,00,00,000
1,000
100
1,00,000
10,000
any hint guys how can i do this, i need to start inserting char(',') from end of the string.
thanks!
Here are two ways, the first using a regular expression, the last just inserting commas in a loop.
a = %w| 100000000 1000 100 100000 10000 |
#=> ["100000000", "1000", "100", "100000", "10000"]
#1 Use a regex
r = /
(?<=\d) # match digit in positive lookbehind
\d{2} # match two digits
(?= # begin positive lookahead
(?:\d{2})* # match two digits, repeated zero or more times
\d # match last digit
\z # match end of string
) # end positive lookahead
/x # extended mode
a.each { |s| puts "#{s} -> #{ s.gsub(r) { |ss| ',' + ss } }" }
100000000 -> 10,00,00,000
1000 -> 1,000
100 -> 100
100000 -> 1,00,000
10000 -> 10,000
This regex is similar to the one given earlier by #Avinish, but I chose to use a positive lookbehind and no capture group, and presented it in extended mode to help readers understand how it worked. I would use a regular expression here.
#2 Insert commas
If you do not wish to use a regular expression, you could determine the position of the last comma to insert (p below), the number of commas to insert (n below) and then insert them, back to front:
def insert_commas(string)
sz = string.size
str = string.dup
p = sz - 3
n = (sz - 2)/2
n.times { str.insert(p, ','); p -= 2 }
str
end
a.each { |s| puts "#{s} -> #{insert_commas(s)}" }
100000000 -> 10,00,00,000
1000 -> 1,000
100 -> 100
100000 -> 1,00,000
10000 -> 10,000
I duped string on the assumption you did not want to mutate string.
Alternatively,
def insert_commas(string)
sz = string.size
return string if sz < 4
p = sz.even? ? 1 : 2
string[0,p] + string[p..-2].gsub(/\d{2}/) { |s| ",#{s}" } + string[-1]
end
Use the below positive lookahead based regex.
gsub(/(\d)(?=(?:\d{2})*\d{3}$)/, "$1,")
DEMO

Resources