Luhn Algorithm in Ruby

1.2k Views Asked by At

I've been trying to implement the Luhn Algorithm in Ruby but keep getting the error message that nil cannot be corerced into Fixnum.

The Luhn Algorithm is supposed to:

Starting with the second to last digit, double every other digit until you reach the first digit

Sum all the untouched digits and the doubled digits (doubled digits need to be broken apart, 10 becomes 1 + 0)

If the total is a multiple of ten, you have received a valid credit card number!

This is what I have:

class CreditCard
  def initialize (card_number)
    if (card_number.to_s.length != 16 )
        raise ArgumentError.new("Please enter a card number with exactly 16 integars")
    end
    @card_number = card_number
    @total_sum = 0
  end

  def check_card
    @new_Array = []
    @new_Array = @card_number.to_s.split('')
    @new_Array.map! { |x| x.to_i }
    @new_Array.each_with_index.map { |x,y| 
      if (y % 2 != 0) 
        x = x*2
      end  
     }  
    @new_Array.map! {|x| 
     if (x > 9)
        x = x-9 
      end  
    }
    @new_Array.each { |x| 
        @total_sum = @total_sum + x
    }  
    if (@total_sum % 10 == 0)
      return true
    else
      return false      
    end  
  end  
end
3

There are 3 best solutions below

0
On
if (x > 9)
  x = x-9
end

=>

x > 9 ? x - 9 : x

or you can also write

if x > 9
  x - 9
else
  x
end

Without else clause value of if false; ...; end will always be nil

0
On

in your section

@new_Array.each_with_index.map { |x,y| 
  if (y % 2 != 0) 
    x = x*2
  end  
}

the changes are not permanent. Also, as Victor wrote, if the test fails, your @new_Array will be filled with nils. At the end

if (@total_sum % 10 == 0)
  return true
else
  return false      
end

you can instead simply write

@total_sum % 10 == 0

since the last line in a ruby method is already a return. I found a slightly different algorithm and implemented it here:

# 1) Reverse the order of the digits in the number.
# 2) Take the first, third, ... and every other odd digit in the reversed digits
#   and sum them to form the partial sum s1
# 3) Taking the second, fourth ... and every other even digit in the reversed digits:
#   a) Multiply each digit by two (and sum the digits if the answer is greater than nine) to form partial sums for the even digits
#   b) Sum the partial sums of the even digits to form s2
# 4) If s1 + s2 ends in zero then the original number is in the form of a valid credit card number as verified by the Luhn test.

def luhn n
  s = n.to_s.reverse
  sum=0
  tmp=0
  (0..s.size-1).step(2) {|k|    #k is odd, k+1 is even
    sum+=s[k].to_i   #s1
    tmp = s[k+1].to_i*2
    tmp = tmp.to_s.split(//).map(&:to_i).reduce(:+) if tmp>9
    sum+=tmp
  }
  sum%10 == 0
end

[49927398716, 49927398717, 1234567812345678, 1234567812345670].each {|num|
  puts "%20s %s" % [num, luhn(num)]
}

#         49927398716 true
#         49927398717 false
#    1234567812345678 false
#    1234567812345670 true

Hope this helps.

0
On

If you don't mind changing your code...

...
def check_card
  sum = 0
  @card_number.to_s.split("").each_with_index do |digit, index|
    d = digit.to_i
    sum += index % 2 == 1 ? d : d * 2 > 9 ? ( d * 2 - 9) : d * 2 
  end
  sum % 10 == 0
end

This just gets the sum of the numbers immediately, and if the index is even(the every other number from second to the last to start is an even number, from an array index' perspective ) doubles it and subtracts 9. Then does the modulo 10 in the end