Luhn algorithm Ruby not recognising AMEX - ruby-on-rails

So I am Having some problems when applying Luhn algorithm
Here are the general rules: http://www.codeproject.com/Tips/515367/Validate-credit-card-number-with-Mod-algorithm
and here is my code
def luhn(credit_card)
result = 0
nums = credit_card.split("")
nums.each_with_index do |item, index|
if index.even?
if item.to_i*2>9
result+= item.to_i*2-9
else
result+= item.to_i*2
end
else
result +=item.to_i
end
end
if (result % 10) == 0
self.validation = "valid"
else
self.validation = "invalid"
end
end
It works on the majority of cards
VISA: 4111111111111111 (valid)
VISA: 4111111111111 (invalid)
VISA: 4012888888881881 (valid)
Discover: 6011111111111117 (valid)
MasterCard: 5105105105105100 (valid)
MasterCard: 5105105105105106 (invalid)
Unknown: 9111111111111111 (invalid)
But when it comes to this one
AMEX: 37828224631000(invalid)
For some reason my code says its not valid,but it should be according to the official testing card list.
I have seen a bunch of other codes that are working but I want to correct the mistake and understand my mistake. I will appreciate some explanation why is it working like this.

Are you sure that your Amex number should be valid?
Can you edit your question to show us where you're getting your testing numbers?
Here's what I see in other Lunh test suites:
"378282246310005" should be true
"37828224631000" should be false
Also, here are items for you to look at:
Your code is moving through the numbers the wrong way: you're moving left-to-right, whereas Lunh is right-to-left.
Your code iterating on the check digit, whereas Lunh doesn't iterate on the check digit.
Try peeling off the check digit before you loop, and reversing your order, such as:
def luhn(credit_card)
(*digits, checksum_digit) = s.split('').map(&:to_i)
result = 0
digits.reverse.each_with_index do |item, index|
…
After you calculate the sum, then add the checksum digit, then compare % 10.

Related

Finding the number of digits in a number restricted number of tools since I am a Python beginner

def digits(n):
total=0
for i in range(0,n):
if n/(10**(i))<1 and n/(10**(i-1))=>1:
total+=i
else:
total+=0
return total
I want to find the number of digits in 13 so I do the below
print digits(13)
it gives me $\0$ for every number I input into the function.
there's nothing wrong with what I've written as far as I can see:
if a number has say 4 digits say 1234 then dividing by 10^4 will make it less than 1: 0.1234 and dividing by 10^3 will make it 1.234
and by 10^3 will make it 1.234>1. when i satisfies BOTH conditions you know you have the correct number of digits.
what's failing here? Please can you advise me on the specific method I've tried
and not a different one?
Remember for every n there can only be one i which satisfies that condition.
so when you add i to the total there will only be i added so total returning total will give you i
your loop makes no sense at all. It goes from 0 to exact number - not what you want.
It looks like python, so grab a solution that uses string:
def digits(n):
return len(str(int(n))) # make sure that it's integer, than conver to string and return number of characters == number of digits
EDIT:
If you REALLY want to use a loop to count number of digits, you can do this this way:
def digits(n):
i = 0
while (n > 1):
n = n / 10
++i
return i
EDIT2:
since you really want to make your solution work, here is your problem. Provided, that you call your function like digits(5), 5 is of type integer, so your division is integer-based. That means, that 6/100 = 0, not 0.06.
def digits(n):
for i in range(0,n):
if n/float(10**(i))<1 and n/float(10**(i-1))=>1:
return i # we don't need to check anything else, this is the solution
return null # we don't the answer. This should not happen, but still, nice to put it here. Throwing an exception would be even better
I fixed it. Thanks for your input though :)
def digits(n):
for i in range(0,n):
if n/(10**(i))<1 and n/(10**(i-1))>=1:
return i

Test variable for Numeric in one line

I found this code on another thread.
def is_number? string
true if Float(string) rescue false
end
Instead of using a method to return true or false, I'd like to do this "is_numeric" test in one line, in an if statement. Can someone explain if this is possible? I'm getting errors at the moment both when the string variable is null and when it contains non-numeric characters.
if Float(string)
* do something
else
* do something else
end
if Float() is pointless code, since Float() will either return a truthy value or raise an error (based on my limited look at the source code - as of writing, you can follow the code path from line #2942). I'd suggest you're asking the wrong question/looking at the problem wrong (it'd be helpful to know what you're actually trying to achieve).
To do something with Float() on one line and avoid breaking code, use rescue as a statement modifier, as has been done in the is_number? method posted.
Float(string) rescue 0.0 # trying to emulate String#to_f!
Ensuring the phone number is 10 digits, numbers only, is quite simple.
PHONE_NUM_LENGTH = 10
string.length == PHONE_NUM_LENGTH && string.count('0-9') == PHONE_NUM_LENGTH
will return the true/false value representing this check. This is more efficient than a Regex.
The first part,
string.length == PHONE_NUM_LENGTH
checks whether the string is 10 characters long. The second,
string.count('0-9') == PHONE_NUM_LENGTH
checks whether it has exactly 10 numeric characters.

BigDecimal to Currency with -0.0

I am working on reports for a website and I am currently thinking of what would be the best way to handle BigDecimal -0.0's.
The database I'm working with has a lot of them. When these -0.0's are put through number_to_currency(), I get "$-0.00". My format for negative numbers is actually "-$x.xx", so note that number_to_currency is not formatting it as a negative number (otherwise there would also be a negative sign in front of the dollar sign), but for some reason the negative sign is being translated along with the 0.
Right now my solution is to do this every time I get an amount from the database:
amount *= -1 if amount == 0 && amount.sign == -1
This changes the -0.0 to a 0.0. It's simple enough, but I can't help but wonder if there is a better solution, or something on BigDecimals or number_to_currency to handle this situation that I'm just not finding.
That is so because the number is converted into a string to be displayed. And:
# to_d converts to BigDecimal, just FYI
"-0".to_d.to_s #=> "-0.0"
Therefore you will have to make it a 0 yourself. But the sign-checks are redundant - a simple comparison with 0 will do the trick:
bdn = "-0".to_d # or BigDecimal.new("-0")
value = bdn.zero? ? 0 : bdn
number_to_currency(value, other_options)
However, you wouldn't want to manually add this check everywhere you're calling number_to_currency. It would be more convenient to create your own modified_number_to_currency method, in your ApplicationHelper, like so:
def modified_number_to_currency( number, options )
value = number.zero? ? 0 : number
number_to_currency(value, options)
end
And then use modified_number_to_currency instead of number_to_currency.
Alternatively, you could overwrite number_to_currency and have it call super in the end. That might also work but I'm not 100% certain.
Coming to your check specifically:
amount *= -1 if amount == 0 && amount.sign == -1
It should simply be:
amount = 0.to_d if amount.zero? # the to_d might or might not be required

appending random integer to a string using .concat

I'm trying to append an integer gotten from a method to a string, I have tried multiple different things: << and .concat (although the same) += as well
I have chosen to make the method due to a function I'm working on that will be added later on
the problem with this code is that instead of returning my string + firstrandomnumber + secondrandomnumber so on so forth it only returns my string + latestrandomnumber
def machineSlot()
tal1= rand(0..10)
return tal1
end
#makes the a random number
if startBool == true
#game startof
gameRunner=true
puts 'pull the lever with x'
leverPullTry =gets.chomp
while gameRunner
#keeps game running
i=1
slotThread='Your numbers are:'
#initialise game rollcount i and string that keeps the numbers
if leverPullTry=='x'
slotThread.concat( machineSlot.to_s)
#if x was entered the slotThread appends a random number
puts slotThread
#slotThreads current process
puts 'pull the lever with x again'
i+=1
#number of rolls increased
leverPullTry =gets.chomp
else
puts 'try again use type x to pull'
leverPullTry =gets.chomp
#user didnt manage to input anything correct
end
end
end
Change these two lines:
slotThread.concat( machineSlot.to_s)
#if x was entered the slotThread appends a random number
puts slotThread
#slotThreads current process
to
puts "#{slotThread}#{machineSlot}"
That's called string interpolation, something used a lot in ruby.
By the way, you're using CamelCase to name your variables and methods which is not the convention. Not urgent but I'd check out this when you have time.

How to count the number of decimal places in a Float?

I am using Ruby 1.8.7 and Rails 2.3.5.
If I have a float like 12.525, how can a get the number of digits past the decimal place? In this case I expect to get a '3' back.
Something like that, I guess:
n = 12.525
n.to_s.split('.').last.size
You should be very careful with what you want. Floating point numbers are excellent for scientific purposes and mostly work for daily use, but they fall apart pretty badly when you want to know something like "how many digits past the decimal place" -- if only because they have about 16 digits total, not all of which will contain accurate data for your computation. (Or, some libraries might actually throw away accurate data towards the end of the number when formatting a number for output, on the grounds that "rounded numbers are more friendly". Which, while often true, means it can be a bit dangerous to rely upon formatted output.)
If you can replace the standard floating point numbers with the BigDecimal class to provide arbitrary-precision floating point numbers, then you can inspect the "raw" number:
> require 'bigdecimal'
=> true
> def digits_after_decimal_point(f)
> sign, digits, base, exponent = f.split
> return digits.length - exponent
> end
> l = %w{1.0, 1.1, 1000000000.1, 1.0000000001}
=> ["1.0,", "1.1,", "1000000000.1,", "1.0000000001"]
> list = l.map { |n| BigDecimal(n) }
=> [#<BigDecimal:7f7a56aa8f70,'0.1E1',9(18)>, #<BigDecimal:7f7a56aa8ef8,'0.11E1',18(18)>, #<BigDecimal:7f7a56aa8ea8,'0.1000000000 1E10',27(27)>, #<BigDecimal:7f7a56aa8e58,'0.1000000000 1E1',27(27)>]
> list.map { |i| digits_after_decimal_point(i) }
=> [0, 1, 1, 10]
Of course, if moving to BigDecimal makes your application too slow or is patently too powerful for what you need, this might overly complicate your code for no real benefit. You'll have to decide what is most important for your application.
Here is a very simple approach. Keep track of how many times you have to multiple the number by 10 before it equals its equivalent integer:
def decimals(a)
num = 0
while(a != a.to_i)
num += 1
a *= 10
end
num
end
decimals(1.234) # -> 3
decimals(10/3.0) # -> 16
Like This:
theFloat.to_s.split(".")[1].length
It is not very pretty, but you can insert it as a method for Float:
class Float
def decimalPlaces
self.to_s.split(".")[1].length
end
end
Can you subtract the floor and then just count how many characters left?
(12.525 -( 12.52­5.floor )).to­_s.length-­2
=> 3
edit: nope this doesnt work for a bunch of reasons, negatives and 0.99999 issues
Olexandr's answer doesn't work for integer. Can try the following:
def decimals(num)
if num
arr = num.to_s.split('.')
case arr.size
when 1
0
when 2
arr.last.size
else
nil
end
else
nil
end
end
You can use this approach
def digits_after_decimal_point(n)
splitted = n.to_s.split(".")
if splitted.count > 1
return 0 if splitted[1].to_f == 0
return splitted[1].length
else
return 0
end
end
# Examples
digits_after_decimal_point("1") #=> 0
digits_after_decimal_point("1.0") #=> 0
digits_after_decimal_point("1.01") #=> 2
digits_after_decimal_point("1.00000") #=> 0
digits_after_decimal_point("1.000001") #=> 6
digits_after_decimal_point(nil) #=> 0

Resources