convert an input string to an integer - ruby

I need to convert an input string to an integer using a get_integer_from_string() function.
I am using Ruby
and I have this schedule.
based on the schedule we need to covert from base 7 to base 10
Please note :
1- If you encounter a string with no numerical sequences, it should return zero (0).
2- The string can contain additional characters after those that form the integral number, which are ignored and have no effect on the behavior of this function.
Thank you for helping me with my homework.

As Tim pointed out above, your table shows base 7, not 6. Both String#to_i and Fixnum#to_s take an optional radix (base) argument:
p "020".to_i(7)
# => 14
p 14.to_s(7)
# => "20"
In order to deal with trailing characters that aren't 0-6, String#slice (a.k.a. String#[]) works well:
expr = /^[0-6]*/
p "72"[expr].to_i(7)
# => 0
p "0202xyz"[expr].to_i(7)
# => 100
p "27"[expr].to_i(7)
# => 2

What you want to do is convert the string (which represents a base 7 number) to a base 10 integer. This can be done via
"021".to_i(7)
# => 15
Please see the Documentation of Ruby String class for the method to_i()

i made this not sure if it's right
def get_integer_from_string(str)
return 0 if (/^(?<num>\d+)$/ =~ str).nil?
result = 0
str.reverse.each_char.with_index do |char, index|
tmp = char.to_i * 7**index
result += tmp
end
result rescue 0
end

Related

How to count the number of zeros at the start of a string

I have a string:
"0011HelloWor00ld001"
How do I count the number of zeros in the starting of the string?
For example, the above string should return 2.
I tried .match(/^[0]+/).size but it doesn't work.
.match(/^0+/) will return a MatchData object, and thus you get 1 as the result (it denotes the number of elements in the match array).
You need to get the size of the match itself. Use one of the following:
"0011HelloWor00ld001".match(/^0+/)[0].size
"0011HelloWor00ld001"[/^0+/].size
"0011HelloWor00ld001".match(/^0+/).to_s.size
You could also simply use the index method of String like
str = '0011HelloWor00ld001'
# as noted by #steenslag if the full string is zeros index will return nil
# solve by returning full string length
str.index(/[^0]/) || str.length
#=> 2
I like #Wiktor's middle answer best, but here are three more.
#1
def nbr_leading_zeros(str)
str.split(/[^0]/).first.size
end
nbr_leading_zeros '0011Hello00' #=> 2
nbr_leading_zeros '101' #=> 0
nbr_leading_zeros '000' #=> 3
#2
def nbr_leading_zeros(str)
str.each_char.take_while { |c| c=='0' }.size
end
#3
def nbr_leading_zeros(str)
str.match(/\A0*/)&.end(0).to_i
end

Division: Ruby no method error

I am trying to run this:
def ArithGeo(arr)
if arr[2]/arr[1] == arr[3]/arr[2]
return "Geometric"
else
return "Arithmetic"
end
end
print ArithGeo(STDIN.gets)
It comes back with line 2 having an "undefined method" when I run this in terminal.
Why? The array is all numbers when testing with [1,2,3,100]. (And yes I know that this will return "Arithmetic" when it isn't. I haven't gotten to that part yet.)
Also, is to_i necessary? If items in an array are already considered an integer, they're an integer, right? I also tried with to_i on each array item but it returned a "division by zero" error even when none of the items in the array were 0, and I wasn't using position 0.
One (or more) of the elements in your arr is a String.
irb(main):009:0> "string"/"another string"
NoMethodError: undefined method `/' for "string":String
When you call to_i on a String it becomes 0.
irb(main):013:0* "string".to_i
=> 0
When you divide by 0 you get an error because you can't do that.
irb(main):011:0> "string".to_i/"another string".to_i
ZeroDivisionError: divided by 0
You can fix your code by changing this line:
print ArithGeo(STDIN.gets)
to this:
print ArithGeo(STDIN.gets.strip.split(',').map(&:to_i))
Then enter your inputs like this:
1,2,3,100 # don't include the "[]" around the numbers
Since your input is of ruby syntax [1,2,3,100] you need to evaluate it.
def ArithGeo(arr)
puts "#{arr.class} , #{arr}"
if arr[2]/arr[1] == arr[3]/arr[2]
return "Geometric"
else
return "Arithmetic"
end
end
puts ArithGeo(eval STDIN.gets )
The input:
[1, 2, 3, 100]
The result:
Array , [1, 2, 3, 100]
Arithmetic
Also , I would recommend using floats to prevent integer rounding.
if arr[2].to_f/arr[1] == arr[3].to_f/arr[2]
Edit:
A much better (safer + more generic) is to use:
JSON.parse( array_string )
For example:
JSON.parse("[1 , 2]")
=> [1, 2]
JSON.parse("[1 , 2]").class
=> Array
And if you really want to be on the safe side , you'll need to add exception handling for JSON parsing errors.
You're passing a String to your method (IO.gets returns a string) when what you really want is an array of integers. If you just pass in this string, you will find that division is not defined for Strings. If you attempt to convert the input to an integer first, any leading non-numeric characters will cause the string to be converted to 0.
Try
arr = STDIN.gets.split(', ').map(&:to_i)
ArithGeo(arr)
It depends on your arr elements. Say, if arr elements are strings, then you will get a undefined method '/' for String (NoMethodError).
You need to make sure your arr elements are numbers i.e. integer or floats etc. on which the division (/) method is implemented.
Update
You can input the values comma separated and convert that string to an array using String#split method as I mentioned in the comment:
def ArithGeo(str)
arr = str.split(',').map(&:to_i) # split the values by comma and make them integer and put in array arr
# then you can use the following beause now your arr is an array, make sure you have at least 4 elements as you
# used index 3 in the calculation
if arr[2]/arr[1] == arr[3]/arr[2]
return "Geometric"
else
return "Arithmetic"
end
end
print ArithGeo(STDIN.gets)

Ruby function that changes each letter for a string and also capitalizes vowels?

I am trying to do a challenge on Coderbyte. The question states:
Have the function LetterChanges(str) take the str parameter being passed and modify it using the following algorithm. Replace every letter in the string with the letter following it in the alphabet (ie. c becomes d, z becomes a). Then capitalize every vowel in this new string (a, e, i, o, u) and finally return this modified string.
Here is my code:
hash = {"a" => 1,"b" => 2,"c" => 3,"d" => 4,"e" => 5,"f" => 6,"g" => 7,"h" => 8,"i" => 9,"j" => 10, "k" => 11,"l" => 12,"m" => 13,"n" => 14,"o" => 15,"p" => 16,"q" => 17,"r" => 18,"s" => 19,"t" => 20,"u" => 21,"v" => 22,"w" => 23,"x" => 24,"y" => 25,"z" => 26}
def LetterChanges(str)
chars = str.split("")
newstr = Array.new
i = 0
newletter = 0
while i <= chars.length
if hash.has_key?(chars[i]) == true #I think this is where the problem is
newletter = hash[chars[i]] + 1
newstr.push(has.key(newletter))
if newstr[i].include?('a','e','i','o','u')
newstr[i].upcase!
end
else
newstr.push(chars[i])
end
i += 1
end
return newstr
end
It keeps saying there is an error with 'has_key?'. I also tried using '.include?' and 'chars[i] =~ [a-zA-Z]' but all return an error. I'm not sure why it isn't accepting any of these methods/regex. If you do decide to answer using regular expressions, please explain in details because they still confuse me a little.
Thanks in advance.
***EDIT: I have taken all of your advice and thought I had a working code, but apparently not. =/
I get this error: (eval):8: undefined method key' for #<Hash:0x149bf0> (NoMethodError) from (eval):4:ineach' from (eval):4:in `LetterChanges' from (eval):18
1 def LetterChanges(str)
2 hash = {"a" => 0,"b" => 1,"c" => 2,"d" => 3,"e" => 4,"f" => 5,"g" => 6,"h" => 7,"i" => 8,"j" => 9, "k" => 10,"l" => 11,"m" => 12,"n" => 13,"o" => 14,"p" => 15,"q" => 16,"r" => 17,"s" => 18,"t" => 19,"u" => 20,"v" => 21,"w" => 22,"x" => 23,"y" => 24,"z" => 25}
3 newstr = Array.new
4 newletter = 0
5 str.each do |i|
6 if hash.has_key?(str[i])
7 newletter = hash[str[i]] + 1
8 newletter = 0 if newletter == 25
9 newstr.push(hash.key(newletter))
10 newstr[i].upcase! if newstr[i] =~ /[aeiou]/
11 else
12 newstr.push(str[i])
13 end
14 end
15 return newstr.to_s
16end
First of all I don't know Coderbyte but it's common practice in Ruby to use snake_case for naming methods (but that's not important here)
The biggest point is this: As soon as you get a 'z' on your input string, you're gonna have a baaad time: In the Line newletter = hash[chars[i]] + 1 you correctly determine the letter's ID (hash[chars[i]], which results in 26) and add 1. However, when converting back to a letter (in the line which I assume should be like this: newstr.push(hash.key(newletter)) - you mistyped hash) you reference a value (27) which does not exist in the hash!
Next, you use include? the wrong way around. Note that include? only takes one argument. We actually have two ways to check if a char is a vovel:
letter.upcase! if %{a e i o u}.include?(letter)
(note that %w{a e i o u} constructs an array containing all the letters). Or:
letter.upcase! if letter =~ /[aeiou]/
(=~ checks if letter matches the RegEx /[aeiou]/)
This should help you get it working!
Here's a working example (but try solving it yourself first!!!) https://gist.github.com/mhutter/8678067
Here are some Tips:
When using Hashes with numbers, it's good practice to make them Zero-Indexed: the lowest number (in your case 'a' => 1 should always be 0).
The % operator returns the remainder of a division. This is especially handy when incrementing indexes of an array. array[(i+1) % array.length] will always get you a valid value.
Your line if hash.has_key?(chars[i]) == true will work as expected, BUT Hash#has_key? already returns true or false so no need for checking for true. Simply write if hash.has_key?(chars[i]).
Strings can be accessed like this: "Foobar"[3], so there's no need splitting them up.
BUT if you do so, you can then do this:
chars = str.split ""
chars.each do |char|
# do something with char
end
however, if you do NOT split your string :), you can replace this:
i = 0
while i <= chars.length
# current char is char[i]
with this:
0.upto(chars.length) do |i|
# current char is char[i]
Constructs like this:
if something?
do_something
end
can be shortened to this:
do_something if something?
I believe your problem with has_key? is because hash is not in the scope of that method. Move the hash={blah} to the line after the def, and also take snowe2010's advice on refactoring the code to make it more ruby like.
I think you meant to put newstr.push(hash.key(newletter)) not newstr.push(has.key(newletter)).
Also, you declare hash as a class variable and not an instance variable. Try puts hash inside the function and you'll see what I mean.
I would also suggest changing your method definition to follow a few ruby conventions. In ruby, we usually use snake case unless it's a class, i.e. def letter_changes(str).
We also don't use while if we don't have to. You can change that code to look like this
chars.each_with_index do |current_char, i|
if hash.has_key?(current_char)
newletter = hash[current_char] + 1
newstr.push(hash.key(newletter))
newstr[i].upcase! if newstr[i].include?('a','e','i','o','u')
else
newstr.push(current_char)
end
end
And we also don't usually return explicitly unless it's from within an if/else/unless/case statement.
There is a pretty easy two line way to do that....
def LetterChanges(str)
str.tr!('a-y','b-z')
str.tr!('aeiou','AEIOU')
return str
end
Nate Beers' two-line solution above is almost perfect but doesn't seem to wrap z -> a. Unfortunately, I don't have the reputation to leave this correction as a comment.
You can solve this by modifying his code to:
def LetterChanges(str)
str.tr!('a-z','b-za')
str.tr!('aeiou','AEIOU')
return str
end
Is this a typo?
newstr.push(has.key(newletter)) instead of hash.key
If that's not the problem, post the error with 'has_key?'.

'Ruby' way to check for regex match before using captures

In a Ruby Sinatrat app, I'm looking to retrieve some numbers associated with strings that may or may not be present in my input. For example: "Cruisers #" might or might not be present, and the # may be any integer.
In addition, where commas would be in normal English number notation (1,000 for one thousand), there will be a period in this notation (1.000 for one thousand).
match = /Cruiser\s*([\d.]*)/m.match(report)
match ?
self.cruiser = match.captures[0].gsub(".", "") :
self.cruiser = 0
Seems like there should be a more compact, 'Ruby'ish way to do this - specifically, I'm looking for a way to combine the regex.match call and the conditional assignment into one statement. Is this, or any other refactoring, possible here? Thanks.
def get_cruiser(str)
if str =~ /Cruiser\s*([\d.]*)/m
$1.gsub(".","")
else
0
end
end
puts get_cruiser("Cruiser 23.444.221")
puts get_cruiser("Crusier")
prints:
23444221
0
There was a problem. Updated.
report1 = 'Cruiser 23.444.221'
report2 = 'Cruiser'
report3 = ''
report4 = '23/04/2010 Cruiser 23.444.221'
class String
def cruiser_count; self[/Cruiser\s*[\d.]*/].to_s.scan(/\d+/).join.to_i end
end
p report1.cruiser_count # => 23444221
p report2.cruiser_count # => 0
p report3.cruiser_count # => 0
p report4.cruiser_count # => 23444221
The following 1 liner is all you need
'1234 Cruiser 1.222'.match(/Cruiser\s*([\d.]*)/).nil? ? 0 : $1.gsub('.', '').to_i
=> 1222

How do I generate a random 10 digit number in ruby?

Additionally, how can I format it as a string padded with zeros?
To generate the number call rand with the result of the expression "10 to the power of 10"
rand(10 ** 10)
To pad the number with zeros you can use the string format operator
'%010d' % rand(10 ** 10)
or the rjust method of string
rand(10 ** 10).to_s.rjust(10,'0')
I would like to contribute probably a simplest solution I know, which is a quite a good trick.
rand.to_s[2..11]
=> "5950281724"
This is a fast way to generate a 10-sized string of digits:
10.times.map{rand(10)}.join # => "3401487670"
The most straightforward answer would probably be
rand(1e9...1e10).to_i
The to_i part is needed because 1e9 and 1e10 are actually floats:
irb(main)> 1e9.class
=> Float
DON'T USE rand.to_s[2..11].to_i
Why? Because here's what you can get:
rand.to_s[2..9] #=> "04890612"
and then:
"04890612".to_i #=> 4890612
Note that:
4890612.to_s.length #=> 7
Which is not what you've expected!
To check that error in your own code, instead of .to_i you may wrap it like this:
Integer(rand.to_s[2..9])
and very soon it will turn out that:
ArgumentError: invalid value for Integer(): "02939053"
So it's always better to stick to .center, but keep in mind that:
rand(9)
sometimes may give you 0.
To prevent that:
rand(1..9)
which will always return something withing 1..9 range.
I'm glad that I had good tests and I hope you will avoid breaking your system.
Random number generation
Use Kernel#rand method:
rand(1_000_000_000..9_999_999_999) # => random 10-digits number
Random string generation
Use times + map + join combination:
10.times.map { rand(0..9) }.join # => random 10-digit string (may start with 0!)
Number to string conversion with padding
Use String#% method:
"%010d" % 123348 # => "0000123348"
Password generation
Use KeePass password generator library, it supports different patterns for generating random password:
KeePass::Password.generate("d{10}") # => random 10-digit string (may start with 0!)
A documentation for KeePass patterns can be found here.
Just because it wasn't mentioned, the Kernel#sprintf method (or it's alias Kernel#format in the Powerpack Library) is generally preferred over the String#% method, as mentioned in the Ruby Community Style Guide.
Of course this is highly debatable, but to provide insight:
The syntax of #quackingduck's answer would be
# considered bad
'%010d' % rand(10**10)
# considered good
sprintf('%010d', rand(10**10))
The nature of this preference is primarily due to the cryptic nature of %. It's not very semantic by itself and without any additional context it can be confused with the % modulo operator.
Examples from the Style Guide:
# bad
'%d %d' % [20, 10]
# => '20 10'
# good
sprintf('%d %d', 20, 10)
# => '20 10'
# good
sprintf('%{first} %{second}', first: 20, second: 10)
# => '20 10'
format('%d %d', 20, 10)
# => '20 10'
# good
format('%{first} %{second}', first: 20, second: 10)
# => '20 10'
To make justice for String#%, I personally really like using operator-like syntaxes instead of commands, the same way you would do your_array << 'foo' over your_array.push('123').
This just illustrates a tendency in the community, what's "best" is up to you.
More info in this blogpost.
I ended up with using Ruby kernel srand
srand.to_s.last(10)
Docs here: Kernel#srand
Here is an expression that will use one fewer method call than quackingduck's example.
'%011d' % rand(1e10)
One caveat, 1e10 is a Float, and Kernel#rand ends up calling to_i on it, so for some higher values you might have some inconsistencies. To be more precise with a literal, you could also do:
'%011d' % rand(10_000_000_000) # Note that underscores are ignored in integer literals
('%010d' % rand(0..9999999999)).to_s
or
"#{'%010d' % rand(0..9999999999)}"
I just want to modify first answer. rand (10**10) may generate 9 digit random no if 0 is in first place. For ensuring 10 exact digit just modify
code = rand(10**10)
while code.to_s.length != 10
code = rand(11**11)
end
Try using the SecureRandom ruby library.
It generates random numbers but the length is not specific.
Go through this link for more information: http://ruby-doc.org/stdlib-2.1.2/libdoc/securerandom/rdoc/SecureRandom.html
Simplest way to generate n digit random number -
Random.new.rand((10**(n - 1))..(10**n))
generate 10 digit number number -
Random.new.rand((10**(10 - 1))..(10**10))
This technique works for any "alphabet"
(1..10).map{"0123456789".chars.to_a.sample}.join
=> "6383411680"
Just use straightforward below.
rand(10 ** 9...10 ** 10)
Just test it on IRB with below.
(1..1000).each { puts rand(10 ** 9...10 ** 10) }
To generate a random, 10-digit string:
# This generates a 10-digit string, where the
# minimum possible value is "0000000000", and the
# maximum possible value is "9999999999"
SecureRandom.random_number(10**10).to_s.rjust(10, '0')
Here's more detail of what's happening, shown by breaking the single line into multiple lines with explaining variables:
# Calculate the upper bound for the random number generator
# upper_bound = 10,000,000,000
upper_bound = 10**10
# n will be an integer with a minimum possible value of 0,
# and a maximum possible value of 9,999,999,999
n = SecureRandom.random_number(upper_bound)
# Convert the integer n to a string
# unpadded_str will be "0" if n == 0
# unpadded_str will be "9999999999" if n == 9_999_999_999
unpadded_str = n.to_s
# Pad the string with leading zeroes if it is less than
# 10 digits long.
# "0" would be padded to "0000000000"
# "123" would be padded to "0000000123"
# "9999999999" would not be padded, and remains unchanged as "9999999999"
padded_str = unpadded_str.rjust(10, '0')
rand(9999999999).to_s.center(10, rand(9).to_s).to_i
is faster than
rand.to_s[2..11].to_i
You can use:
puts Benchmark.measure{(1..1000000).map{rand(9999999999).to_s.center(10, rand(9).to_s).to_i}}
and
puts Benchmark.measure{(1..1000000).map{rand.to_s[2..11].to_i}}
in Rails console to confirm that.
An alternative answer, using the regexp-examples ruby gem:
require 'regexp-examples'
/\d{10}/.random_example # => "0826423747"
There's no need to "pad with zeros" with this approach, since you are immediately generating a String.
This will work even on ruby 1.8.7:
rand(9999999999).to_s.center(10, rand(9).to_s).to_i
A better approach is use Array.new() instead of .times.map. Rubocop recommends it.
Example:
string_size = 9
Array.new(string_size) do
rand(10).to_s
end
Rubucop, TimesMap:
https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Performance/TimesMap
In my case number must be unique in my models, so I added checking block.
module StringUtil
refine String.singleton_class do
def generate_random_digits(size:)
proc = lambda{ rand.to_s[2...(2 + size)] }
if block_given?
loop do
generated = proc.call
break generated if yield(generated) # check generated num meets condition
end
else
proc.call
end
end
end
end
using StringUtil
String.generate_random_digits(3) => "763"
String.generate_random_digits(3) do |num|
User.find_by(code: num).nil?
end => "689"(This is unique in Users code)
I did something like this
x = 10 #Number of digit
(rand(10 ** x) + 10**x).to_s[0..x-1]
Random 10 numbers:
require 'string_pattern'
puts "10:N".gen

Resources