Refactor Ruby code for Luhn algorithm - ruby

Help me refactor implementing Luhn algorithm, which is described as follows:
The formula verifies a number against its included check digit, which
is usually appended to a partial account number to generate the full
account number. This account number must pass the following test:
From the rightmost digit, which is the check digit, moving left, double the value of every second digit; if the product of this doubling operation is greater than 9 (e.g., 8 × 2 = 16), then sum the digits of the products (e.g., 16: 1 + 6 = 7, 18: 1 + 8 = 9).
Take the sum of all the digits.
If the total modulo 10 is equal to 0 (if the total ends in zero) then the number is valid according to the Luhn formula; else it is not valid.
Assume an example of an account number "7992739871" that will have a
check digit added, making it of the form 7992739871x:
Account number 7 9 9 2 7 3 9 8 7 1 x
Double every other 7 18 9 4 7 6 9 16 7 2 -
Sum of digits 7 9 9 4 7 6 9 7 7 2 =67
The check digit (x) is obtained by computing the sum of digits then
computing 9 times that value modulo 10 (in equation form, (67 × 9 mod
10)). In algorithm form:
Compute the sum of the digits (67).
Multiply by 9 (603).
The last digit, 3, is the check digit. Thus, x=3.
Following is my implementation, it works but could be a lot better, I believe.
def credit_check(num)
verify = num.to_s.split('').map(&:to_i)
half1 = verify.reverse.select.each_with_index { |str, i| i.even? }
half1 = half1.inject(0) { |r,i| r + i }
# This implements rule 1
half2 = verify.reverse.select.each_with_index { |str, i| i.odd? }
double = half2.map { |n| n * 2 }
double = double.map { |n| n.to_s.split('') }
double = double.flatten.map(&:to_i)
double = double.inject(0) { |r,i| r + i }
final = double + half1
puts final % 10 == 0 && (num.to_s.length > 12 && num.to_s.length < 17) ? "VALID" : "INVALID"
end
I'm a rank noob at all of this, obviously. But I appreciate any help, including proper style!

Suggestions:
Try to encapsulate your code in a class and provide a intuitive public API. Hide the inner details of the algorithm in private methods.
Break the rules into small methods in the class that has utmost 5 lines, break this rule sparingly. Follow Sandi Metz Rules.
Study the problem and find domain names relevant to the problem; use it to name the small methods.
Focus on readability. Remember this quote: "Programs must be written for people to read, and only incidentally for machines to execute." by Hal Abelson from SICP.
Read Ruby style guide to improve code formatting; and yes get a better editor.
Following these may seem like making the code more verbose. But it will improve readability and help for maintenance. Also, if you tend to follow it even in personal projects, this process will be etched into you and will soon become second nature.
With these in mind, go through the following attempt at the problem:
class CreditCard
VALID_LENGTH_RANGE = 12..17
def initialize(number)
#number = number.to_s
end
def valid?
valid_length? && check_sum_match?
end
private
def valid_length?
VALID_LENGTH_RANGE.include? #number.length
end
def check_sum_match?
check_sum.end_with? check_digit
end
def check_sum
digits = check_less_number
.reverse
.each_char
.each_with_index
.map do |character, index|
digit = character.to_i
index.even? ? double_and_sum(digit) : digit
end
digits.reduce(:+).to_s
end
def check_less_number
#number[0..-2]
end
def check_digit
#number[-1]
end
def double_and_sum(digit)
double = digit * 2
tens = double / 10
units = double % 10
tens + units
end
end
Hence you can use it as follows:
CreditCard.new(222222222224).valid? # => true
CreditCard.new(222222222222).valid? # => false

how about using nested inject method
half2 = verify.reverse.select.each_with_index { |str, i| i.odd? }
double = half2.map { |n| n * 2 }
double = double.inject(0){|x,y| x + y.to_s.split("").inject(0){|sum, n| sum + n.to_i}}

I would implement that algorithm like that:
def credit_card_valid?(num)
digits = String(num).reverse.chars.map(&:to_i)
digits.each_with_index.reduce(0) do |acc, (value, index)|
acc + if index.even?
value
else
double_value = value * 2
if double_value > 9
double_value.to_s.split('').map(&:to_i).reduce(&:+)
else
double_value
end
end
end % 10 == 0
end
Well, that code works for those examples from Wikipedia :)
Here are some advices for you:
get rid of prints/puts to stdin in your function, just return a
value. For this function boolean true/false is good.
ruby community
uses '?' in method names that return false/true
don't forget about
properly formatting your code, but maybe you might've not yet learnt how to do it on Stackoverflow (I haven't yet :)
use 2 spaces for indenting your code

Related

Combining rand() numbers

I'm attempting to combine a few random numbers. The numbers are supposed to represent a dice.
For example, I want to roll the dice 5 times and I get the following results:
4 2 1 4 6.
These are all individual numbers but what I want to do is combine it together as 42146.
This might be a very simple question so I apologize for that but I'm still new to Ruby.
I'm generating numbers between [1,6] through this:
number = 1 + rand(6)
1.upto(5).map { rand(1..6) }.join.to_i
1.upto(5) will give you an Enumerator for 5 elements
map { rand(1..6) } will map a random number between 1 and 6 to each of the above elements
join will concatenate all elements of what you got so far
to_i will transform the above result from string to integer
Although all the answers are correct, there is one more option:
dice = 0
5.times do
dice = dice * 10 + rand(1..6)
end
puts dice
Demo: http://ideone.com/WfagFT
This time you treat everything as integers. Everytime (5 rolls) you take the result variable, multiply it by 10 and then add new random number at the end of it. There's no need to use anything else for that.
Of course if you need it, this code can be also written as one-liner and do exactly the same as above:
p #dice if 5.times { #dice = #dice.to_i * 10 + rand(1..6) }
5.times.inject(0){|n, _| n * 10 + 1 + rand(6)}
p Array.new(5){1 + rand(6)}.join # => "53325"

Using for-loop to add numbers together

If I randomly put in two numbers (first number is smaller), how do I use a for-loop to add all the numbers between and itself?
ex:
first number: 3
second number: 5
the computer should give an answer of '12'.
How do I do that using a for-loop?
In Ruby we seldom use a for loop because it leaves litter behind. Instead, you can very simply do what you want using inject:
(3..5).inject(:+) # => 12
This is using some of the deeper Ruby magic (:+), which is a symbol for the + method and is passed into inject. How it works is a different question and is something you'll need to learn later.
Don't insist on doing something in a language using a particular construct you learned in another language. That will often force non-idiomatic code and will keep you from learning how to do it as other programmers in that language would do it. That creates maintenance issues and makes you less desirable in the workplace.
Simple for loop across the range you defined:
puts "Enter first number: "
first = gets.to_i
puts "Enter second number: "
second = gets.to_i
total = 0
for i in (first..second) do
total += i
end
puts total
Note that if you don't enter a valid number, it will converted to 0. Also this assumes the second number is larger than the first.
In Rails, or in plain-vanilla Ruby with ActiveSupport, you can do something even simpler than a for loop, or than what other people wrote.
(first_num..second_num).sum
This is shorthand for sum in Ruby:
sum = 0
(first_num..second_num).each { |num| sum += num }
first, second = [3,5]
for x in (0..0) do
p (first + second)*(second - first + 1) / 2
end
I know you said for loop, but why not use what Ruby gives you?
> a = 3
> b = 5
> a.upto(b).inject(0) {|m,o| m += o}
=> 12
If you insist on a for loop...
> m = 0
=> 0
> for i in 3..5
* m += i
* end
=> 3..5
> m
=> 12
Since Ruby 2.4 you directly call sum on an Enumerable.
For Example [1, 2, 3].sum #=> 6
In Ruby it's very rare to see a for loop. In this instance a more idiomatic method would be upto:
x = 3
y = 5
total = 0
x.upto(y) do |n|
total += n
end
puts total
# => 12
Another method would be to use reduce:
total = x.upto(y).reduce do |sum, n|
sum += n
end
...which can be shortened to this:
total = x.upto(y).reduce(&:+)

Comparing two Integers by their divisibility

For instance:
8 > 10 = true, since 8 is divisible by 2 three times and 10 only once.
How can I compare two integers from any range of numbers? Are the modulo and divide operator capable of doing this task?
Use binary caculate to judge it
def devided_by_two(i)
return i.to_s(2).match(/0*$/).to_s.count('0')
end
To make integer divisibility by 2, just transcode it to binary and judge how many zero from end of banary number. The code I provide can be more simple I think.
Yes, they are capable. A number is even if, when you divide it by two, the remainder is zero.
Hence, you can use a loop to continuously divide by two until you get an odd number, keeping a count of how many times you did it.
The (pseudo-code) function for assigning a "divisibility by two, continuously" value to a number would be something like:
def howManyDivByTwo(x):
count = 0
while x % 2 == 0:
count = count + 1
x = x / 2 # make sure integer division
return count
That shouldn't be too hard to turn into Ruby (or any procedural-type language, really), such as:
def howManyDivByTwo(x)
count = 0
while x % 2 == 0
count = count + 1
x = x / 2
end
return count
end
print howManyDivByTwo(4), "\n"
print howManyDivByTwo(10), "\n"
print howManyDivByTwo(11), "\n"
print howManyDivByTwo(65536), "\n"
This outputs the correct:
2
1
0
16
Astute readers will have noticed there's an edge case in that function, you probably don't want to try passing zero to it. If it was production code, you'd need to catch that and act intelligently since you can divide zero by two until the cows come home, without ever reaching an odd number.
What value you return for zero depends on needs you haven't specified in detail. Theoretically (mathematically), you should return infinity but I'll leave that up to you.
Notice that you will likely mess up much of your code if you redefine such basic method. Knowing that, this is how it's done:
class Integer
def <=> other
me = self
return 0 if me.zero? and other.zero?
return -1 if other.zero?
return 1 if me.zero?
while me.even? and other.even?
me /= 2
other /= 2
end
return 0 if me.odd? and other.odd?
return -1 if me.odd?
return 1 if other.odd? # This condition is redundant, but is here for symmetry.
end
end

Why does 0 return on this ruby base conversion method

as part of a ruby challenge, I'm making a custom method num_to_s(num,base) which takes a number and base, and returns a string with the new base.
The question gave me the hint that you can find the value of each digit by this operation --
(123 / 10**0) % 10 == 3 # ones place
(123 / 10**1) % 10 == 2 # tens place
(123 / 10**2) % 10 == 1 # hundreds place
So, I created the following function --
def num_to_s(num, base)
values = ["0", "1", "2","3","4","5","6","7","8","9","a","b","c","d","e","f"]
answer_array = []
highest_power = 0
#find the highest power
while base**highest_power <= num
highest_power+=1
end
current_power = 0
#run a loop to find the values for base**0 to the highest power
while current_power <= highest_power
digit = values[ ((num / base**current_power) % base) ]
answer_array << digit
current_power +=1
end
answer_array.reverse.join
end
num_to_s(4,2)
num_to_s(20,16)
When I run this function, everything works great, except sometimes the answer is prefixed by 0. If I were to remove the 0 though, the answer would be correct.
Just out of curiuosity, why does the 0 show up in the method?
Example --
num_to_s(5,2) #=> 0101
While the actual answer is 101
while current_power <= highest_power
this is the problem. You look for the first power of the base higher than num, that means you don't have to consider such power: in your example, highest_power is 3, that means, if you allow current_power to be equal to it, you get 4 iterations {0,1,2,3}, while you only need 3, namely {0,1,2}.
Replace it with
while current_power < highest_power
and you should be fine. The strange part is that you say "sometime" you get a 0, while, in theory, you should get it every single time.
You may be interested in this solution. It uses a constant array Symbols to avoid reassigning the array values every time the method is called. It also makes use of Numeric#divmod, which returns the quotient and the remainder of a division in one operation.
Symbols = ('0' .. '9').to_a + ('a' .. 'z').to_a
def num_to_s(num, base)
ret = ''
while num > 0 or ret == ''
num, digit = num.divmod(base)
ret = Symbols[digit] + ret
end
ret
end
puts num_to_s(4, 2)
puts num_to_s(20, 16)
puts num_to_s(255, 8)
puts num_to_s(44_027, 36)
output
100
14
377
xyz

Simple recursion problem

I'm taking my first steps into recursion and dynamic programming and have a question about forming subproblems to model the recursion.
Problem:
How many different ways are there to
flip a fair coin 5 times and not have
three or more heads in a row?
If some could put up some heavily commented code (Ruby preferred but not essential) to help me get there. I am not a student if that matters, this is a modification of a Project Euler problem to make it very simple for me to grasp. I just need to get the hang of writing recursion formulas.
If you would like to abstract the problem into how many different ways are there to flip a fair coin Y times and not have Z or more heads in a row, that may be beneficial as well. Thanks again, this website rocks.
You can simply create a formula for that:
The number of ways to flip a coin 5 times without having 3 heads in a row is equal to the number of combinations of 5 coin flips minus the combinations with at least three heads in a row. In this case:
HHH-- (4 combinations)
THHH- (2 combinations)
TTHHH (1 combination)
The total number of combinations = 2^5 = 32. And 32 - 7 = 25.
If we flip a coin N times without Q heads in a row, the total amount is 2^N and the amount with at least Q heads is 2^(N-Q+1)-1. So the general answer is:
Flip(N,Q) = 2^N - 2^(N-Q+1) +1
Of course you can use recursion to simulate the total amount:
flipme: N x N -> N
flipme(flipsleft, maxhead) = flip(flipsleft, maxhead, 0)
flip: N x N x N -> N
flip(flipsleft, maxhead, headcount) ==
if flipsleft <= 0 then 0
else if maxhead<=headcount then 0
else
flip(flipsleft - 1, maxhead, headcount+1) + // head
flip(flipsleft - 1, maxhead, maxhead) // tail
Here's my solution in Ruby
def combination(length=5)
return [[]] if length == 0
combination(length-1).collect {|c| [:h] + c if c[0..1]!= [:h,:h]}.compact +
combination(length-1).collect {|c| [:t] + c }
end
puts "There are #{combination.length} ways"
All recursive methods start with an early out for the end case.
return [[]] if length == 0
This returns an array of combinations, where the only combination of zero length is []
The next bit (simplified) is...
combination(length-1).collect {|c| [:h] + c } +
combination(length-1).collect {|c| [:t] + c }
So.. this says.. I want all combinations that are one shorter than the desired length with a :head added to each of them... plus all the combinations that are one shorter with a tail added to them.
The way to think about recursion is.. "assuming I had a method to do the n-1 case.. what would I have to add to make it cover the n case". To me it feels like proof by induction.
This code would generate all combinations of heads and tails up to the given length.
We don't want ones that have :h :h :h. That can only happen where we have :h :h and we are adding a :h. So... I put an if c[0..1] != [:h,:h] on the adding of the :h so it will return nil instead of an array when it was about to make an invalid combination.
I then had to compact the result to ignore all results that are just nil
Isn't this a matter of taking all possible 5 bit sequences and removing the cases where there are three sequential 1 bits (assuming 1 = heads, 0 = tails)?
Here's one way to do it in Python:
#This will hold all possible combinations of flipping the coins.
flips = [[]]
for i in range(5):
#Loop through the existing permutations, and add either 'h' or 't'
#to the end.
for j in range(len(flips)):
f = flips[j]
tails = list(f)
tails.append('t')
flips.append(tails)
f.append('h')
#Now count how many of the permutations match our criteria.
fewEnoughHeadsCount = 0
for flip in flips:
hCount = 0
hasTooManyHeads = False
for c in flip:
if c == 'h': hCount += 1
else: hCount = 0
if hCount >= 3: hasTooManyHeads = True
if not hasTooManyHeads: fewEnoughHeadsCount += 1
print 'There are %s ways.' % fewEnoughHeadsCount
This breaks down to:
How many ways are there to flip a fair coin four times when the first flip was heads + when the first flip was tails:
So in python:
HEADS = "1"
TAILS = "0"
def threeOrMoreHeadsInARow(bits):
return "111" in bits
def flip(n = 5, flips = ""):
if threeOrMoreHeadsInARow(flips):
return 0
if n == 0:
return 1
return flip(n - 1, flips + HEADS) + flip(n - 1, flips + TAILS)
Here's a recursive combination function using Ruby yield statements:
def combinations(values, n)
if n.zero?
yield []
else
combinations(values, n - 1) do |combo_tail|
values.each do |value|
yield [value] + combo_tail
end
end
end
end
And you could use regular expressions to parse out three heads in a row:
def three_heads_in_a_row(s)
([/hhh../, /.hhh./, /..hhh/].collect {|pat| pat.match(s)}).any?
end
Finally, you would get the answer using something like this:
total_count = 0
filter_count = 0
combinations(["h", "t"], 5) do |combo|
count += 1
unless three_heads_in_a_row(combo.join)
filter_count += 1
end
end
puts "TOTAL: #{ total_count }"
puts "FILTERED: #{ filter_count }"
So that's how I would do it :)

Resources