I re-implemented String#to_i to use bytes to store individual characters. My solution is:
def to_i(string)
string.bytes.map { |ascii| ascii - 48 }.inject { |sum, int| sum * 10 + int}
end
to_i("22") # => 22
I was wondering if it was possible to implement to_i with pack and unpack instead.
It seems the only thing pack/unpack can do here is convert the digit string into array of numbers. Besides, the implementation is buggy. It didn't check whether the input is really digit string.
def to_i(string)
string.unpack('C*').reduce(0){|sum, i| sum * 10 + i - 48}
end
Related
How can I confidently increment a string number in Ruby? I understand I can call "1".next and produce "2" but this does not work for negative numbers.
"-3".next
=> "-4"
How can I call to increment both positive and negative string digits? I want to return value as String.
You can convert that string to integer, call #next and then convert it back to string
'-3'.to_i.next.to_s
You could just do ("-3".to_i + 1).to_s but that does not account for float values (if you cared for that). You can call gsub with a block and match with regex to convert and increment. next does not work for negatives as it only evaluates the rightmost numeric and pays no attention to the minus character. The below matches for integers or floats and increments them based on the integer type:
INCREMENTAL = 1
def increment_str(str)
str.gsub(/(-)?\d+(.\d+)?/) { |x| x.include?(".") ? x.to_f + INCREMENTAL : x.to_i + INCREMENTAL }
end
increment_str("-3") => "-2"
increment_str("3") => "4"
increment_str("-3.0") => "-2.0"
increment_str("3.0") => "4.0"
Your answer depends on whether the input is integer or float. In each case, cast it to the appropriate numeric type (to_i to to_f), increment by 1, and cast back to string. For example:
['-1.23', '-1', '0', '1', '1.23'].each do |str|
int_str_plus_1 = (str.to_i + 1).to_s
float_str_plus_1 = (str.to_f + 1).to_s
puts [str, int_str_plus_1, float_str_plus_1].join("\t")
end
Prints:
-1.23 0 -0.22999999999999998
-1 0 0.0
0 1 1.0
1 2 2.0
1.23 2 2.23
I tried SecureRandom.random_number(9**6) but it sometimes returns 5 and sometimes 6 numbers. I'd want it to be a length of 6 consistently. I would also prefer it in the format like SecureRandom.random_number(9**6) without using syntax like 6.times.map so that it's easier to be stubbed in my controller test.
You can do it with math:
(SecureRandom.random_number(9e5) + 1e5).to_i
Then verify:
100000.times.map do
(SecureRandom.random_number(9e5) + 1e5).to_i
end.map { |v| v.to_s.length }.uniq
# => [6]
This produces values in the range 100000..999999:
10000000.times.map do
(SecureRandom.random_number(9e5) + 1e5).to_i
end.minmax
# => [100000, 999999]
If you need this in a more concise format, just roll it into a method:
def six_digit_rand
(SecureRandom.random_number(9e5) + 1e5).to_i
end
To generate a random, 6-digit string:
# This generates a 6-digit string, where the
# minimum possible value is "000000", and the
# maximum possible value is "999999"
SecureRandom.random_number(10**6).to_s.rjust(6, '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 = 1,000,000
upper_bound = 10**6
# n will be an integer with a minimum possible value of 0,
# and a maximum possible value of 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 "999999" if n == 999999
unpadded_str = n.to_s
# Pad the string with leading zeroes if it is less than
# 6 digits long.
# "0" would be padded to "000000"
# "123" would be padded to "000123"
# "999999" would not be padded, and remains unchanged as "999999"
padded_str = unpadded_str.rjust(6, '0')
Docs to Ruby SecureRand, lot of cool tricks here.
Specific to this question I would say: (SecureRandom.random_number * 1000000).to_i
Docs: random_number(n=0)
If 0 is given or an argument is not given, ::random_number returns a float: 0.0 <= ::random_number < 1.0.
Then multiply by 6 decimal places (* 1000000) and truncate the decimals (.to_i)
If letters are okay, I prefer .hex:
SecureRandom.hex(3) #=> "e15b05"
Docs:
hex(n=nil)
::hex generates a random hexadecimal string.
The argument n specifies the length, in bytes, of the random number to
be generated. The length of the resulting hexadecimal string is twice
n.
If n is not specified or is nil, 16 is assumed. It may be larger in
future.
The result may contain 0-9 and a-f.
Other options:
SecureRandom.uuid #=> "3f780c86-6897-457e-9d0b-ef3963fbc0a8"
SecureRandom.urlsafe_base64 #=> "UZLdOkzop70Ddx-IJR0ABg"
For Rails apps creating a barcode or uid with an object you can do something like this in the object model file:
before_create :generate_barcode
def generate_barcode
begin
return if self.barcode.present?
self.barcode = SecureRandom.hex.upcase
end while self.class.exists?(barcode: barcode)
end
SecureRandom.random_number(n) gives a random value between 0 to n. You can achieve it using rand function.
2.3.1 :025 > rand(10**5..10**6-1)
=> 742840
rand(a..b) gives a random number between a and b. Here, you always get a 6 digit random number between 10^5 and 10^6-1.
I am trying to write a Caesar Cipher in Ruby and I hit a snag when trying to change only the letters to a numerical values and not the punctuation marks.
Here is my script so far:
def caesar_cipher(phrase, key)
array = phrase.split("")
number = array.map {|n| n.upcase.ord - (64-key)}
puts number
end
puts "Script running"
caesar_cipher("Hey what's up", 1)
I tried to use select but I couldn't figure out how to select only the punctuation marks or only the letters.
Use String#gsub to match only the characters that you want to replace. In this case it's the letters of the alphabet, so you'll use the regular expression /[a-z]/i.
You can pass a block to gsub which will be called for each match in the string, and the return value of the block will be used as the replacement. For example:
"Hello, world!".gsub(/[a-z]/i) {|chr| (chr.ord + 1).chr }
# => Ifmmp, xpsme!"
Here's a version of your Caesar cipher method that works pretty well:
BASE_ORD = 'A'.ord
def caesar_cipher(phrase, key)
phrase.gsub(/[a-z]/i) do |letter|
orig_pos = letter.upcase.ord - BASE_ORD
new_pos = (orig_pos + key) % 26
(new_pos + BASE_ORD).chr
end
end
caesar_cipher("Hey, what's up?", 1) # => "IFZ, XIBU'T VQ?"
Edit:
% is the modulo operator. Here it's used to make new_pos "wrap around" to the beginning of the alphabet if it's greater than 25.
For example, suppose letter is "Y" and key is 5. The position of "Y" in the alphabet is 24 (assuming "A" is 0), so orig_pos + key will be 29, which is past the end of the alphabet.
One solution would be this:
new_pos = orig_pos + key
if new_pos > 25
new_pos = new_pos - 26
end
This would make new_pos 3, which corresponds to the letter "D," the correct result. We can get the same result more efficiently, however, by taking "29 modulo 26"—expressed in Ruby (and many other languages) as 29 % 26—which returns the remainder of the operation 29 ÷ 26. (because there are 26 letters in the alphabet). 29 % 26 is 3, the same result as above.
In addition to constraining a number to a certain range, as we do here, the modulo operator is also often used to test whether a number is divisible by another number. For example, you can check if n is divisible by 3 by testing n % 3 == 0.
I'm new to learning Ruby, and I'm trying to ask the user for an input, square that number, and then return the sum of the digits in the squared number. I'm confused when to use .to_s and .to_i, and I'd also like to accomplish this without using a shortcut.
Here's what I have so far (which doesn't work) :
def sqr_digit_sum(n)
square = (n ** 2).to_s
no_of_digits = square.size
sum = 0
i = 0
while i < no_of_digits
sum += square[i].to_i
i += 1
end
puts sum
end
I don't get why if I input 9 for a square of 81, square[0] returns 56 instead of 8. Can someone explain this?
It appears you’re using Ruby 1.8, where String#[] with a Fixnum argument (e.g. '8'[0]) returns the decimal ASCII character value. Which for '8' is 56. Since this is already a Fixnum, calling to_i on it has no effect. You should instead pass an index and length to []:
string = '81'
string[0, 1] #=> "8"
string[1, 1] #=> "1"
If you want all the characters in an array, though, you should use chars/each_char:
string = '81'
string.chars.to_a #=> ["8", "1"]
string.chars.to_a.map { |char| char.to_i } #=> [8, 1]
This makes implementing what you want fairly straightforward using the above and reduce:
n = 9
(n ** 2).to_s.chars.to_a.map { |char| char.to_i }.reduce(0, :+) #=> 9
Ultimately, though, you should upgrade to Ruby 2.0 (or at least 1.9.2+) as soon as possible as 1.8 is no longer supported and receives no security updates.
You could do this
def sqr_digit_sum(n)
square = (n ** 2)
puts square.to_s.split(//).inject(0) { |sum, number_as_string| number_as_string.to_i + sum }
end
Your code works just fine. Also, for the following code, the result is 8 as expected:
n = 9
square = (n ** 2).to_s[0]
puts square
Finally, may I suggest to refactor your code as:
def sqr_digit_sum(n)
sum = 0
(n ** 2).to_s.each_char { |c| sum += c.to_i }
puts sum
end
you don't really need a while loop there.
I am looking for a way to convert a base-10 number into a base-N number where N can be large. Specifically i am looking at converting to base-85 and back again. Does anyone know a simple algorithm to perform the conversion? Ideally it would provide something like:
to_radix(83992, 85) -> [11, 53, 12]
Any ideas are appreciated!
Roja
That was kind of an interesting question, so I went a little overboard:
class Integer
def to_base(base=10)
return [0] if zero?
raise ArgumentError, 'base must be greater than zero' unless base > 0
num = abs
return [1] * num if base == 1
[].tap do |digits|
while num > 0
digits.unshift num % base
num /= base
end
end
end
end
This works for arbitrary bases. It only works for integers, although there is no reason why it couldn't be extended to work with any arbitrary number. Also, it ignores the sign of the number. Again, there is no reason why it must do that, but mainly I didn't want to have to come up with a convention for returning the sign in the return value.
class Integer
old_to_s = instance_method(:to_s)
define_method :to_s do |base=10, mapping=nil, sep=''|
return old_to_s.bind(self).(base) unless mapping || base > 36
mapping ||= '0123456789abcdefghijklmnopqrstuvwxyz'
return to_base(base).map {|digit| mapping[digit].to_s }.join(sep)
end
end
[Fixnum, Bignum].each do |klass|
old_to_s = klass.instance_method(:to_s)
klass.send :define_method, :to_s do |base=10, mapping=nil, sep=''|
return old_to_s.bind(self).(base) unless mapping || base > 36
return super(base, mapping, sep) if mapping
return super(base)
end
end
I also extended the to_s method so that it works with bases greater than 36. If you want to use a base greater than 36, you have to pass in a mapping object which maps the "digits" to strings. (Well, actually, all that is required is that you provide an object that responds to [] and returns something which responds to to_s. So, a string is perfect, but e.g. an array of integers also works.)
It also accepts an optional separator, which is used to separate the digits.
For example, this allows you to format an IPv4 address by treating it as a base-256 number and using the identity for the mapping and '.' as the separator:
2_078_934_278.to_s(256, Array.new(256) {|i| i }, '.') # => '123.234.5.6'
Here's an (incomplete) testsuite:
require 'test/unit'
class TestBaseConversion < Test::Unit::TestCase
def test_that_83992_in_base_85_is_11_53_12
assert_equal [11, 53, 12], 83992.to_base(85)
end
def test_that_83992_in_base_37_is_1_24_13_2
assert_equal [1, 24, 13, 2], 83992.to_base(37)
end
def test_that_84026_in_base_37_is_1_24_13_36
assert_equal [1, 24, 13, 36], 84026.to_base(37)
end
def test_that_0_in_any_base_is_0
100.times do |base|
assert_equal [0], 0.to_base(base)
assert_equal [0], 0.to_base(1 << base)
assert_equal [0], 0.to_base(base << base)
end
end
def test_that_84026_in_base_37_prints_1od_
assert_equal '1od_', 84026.to_s(37, '0123456789abcdefghijklmnopqrstuvwxyz_')
end
def test_that_ip_address_formatting_works
addr = 2_078_934_278
assert_equal '123.234.5.6', addr.to_s(256, (0..255).to_a, '.')
assert_equal '123.234.5.6', addr.to_s(256, Array.new(256) {|i| i}, '.')
end
def test_that_old_to_s_still_works
assert_equal '84026', 84026.to_s
assert_equal '1su2', 84026.to_s(36)
end
end
The pseudocode for this is fairly straightforward. To base 85 from unsigned integers:
digits := '';
while (number > 0)
digit := number % 85
digits := base85Digit(digit) + digits
number /= 85 // integer division so the remainder is rounded off
end while
And to base 10:
mult := 1
result := 0
for each digit in digits // starting from the rightmost working left
result += base10(digit) * mult
mult *= 85
end for
Just a general pseudocode algorithm:
initialize empty list
take current number mod base, store result at front of list
divide current number by base and floor it (integer division does this perfectly)
if result is still greater than zero, repeat at #2
83992 / 85 = 988, reminder 12
988 / 85 = 11, reminder 53
11 / 85 = 0, reminder 11
write the reminder in reverse order: 11, 53, 12 to get your base-85 number.
To get it back:
11 * 85^2 + 53 * 85^1 + 12 * 85^0 = 83992
The simplest algorithm that I can think of is (in pseudo-code):
N = base-10 number
1) N mod 85 = 1st number
2) tempVal = floor(N/85)
3) if(tempVal > 0 && tempVal < 85) then
tempVal= 2nd number
else
2nd number = (tempVal mod 85), then goto step (2), replacing N with N1
Base 85 is particularly useful for ASCII encoding of binary data, which I presume is what you're using it for. (However, if this is why you should ask yourself whether it's really worth the extra hassle and whether Base 64 won't be good enough.)
If you're using this as an encoding scheme, your job is going to be to convert integers (4 bytes) into groups of 5 base85 numbers. (How you deal with things that are not multiples of 4 bytes is up to you--usually the end is padded with zeros. See the Wikipedia page on Base 85 for details.)
The basic algorithm is quite simple: take the remainder on division of 85 when packing into base 85, then divide and repeat, until you're done. To go back again, repeatedly add the value and multiply by 85 until you're done. I'm not terribly familiar with Ruby, so the code here is a C/C++/Javaish style, which hopefully you can interpret:
// To base 85
unsigned int n = // your number
byte b85[5]; // What you want to fill
for (int i=0 ; i<5 ; i++) {
b85[4-i] = (n%85); // Fill backwards to get most significant value at front
n = n/85;
}
// From base 85
n = 0;
for (int i=0 ; i< 5 ; i++) {
n = n*85 + b85[i];
}
This is without worrying about overflow, without worrying about adding 33 to get into ASCII range, and without worrying about the convention that zero is encoded as z not !!!!!, and so on.
because I feel recursion is under-represented in the answers I give the following rough draft
def to_radix(int, radix)
int == 0 ? [] : (to_radix(int / radix, radix) + [int % radix])
end
Fixnum#to_s won't help you, as it only goes up to base 36.
I'm surprised that you're going up to base 85. Can you explain how radixs work?