Implementation of Luhn algorithm
Asked Answered
S

14

16

I am trying to implement simple validation of credit card numbers. I read about the Luhn algorithm on Wikipedia:

  1. Counting from the check digit, which is the rightmost, and moving left, double the value of every second digit.
  2. Sum the digits of the products (e.g., 10: 1 + 0 = 1, 14: 1 + 4 = 5) together with the undoubled digits from the original number.
  3. 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.

On Wikipedia, the description of the Luhn algorithm is very easily understood. However, I have also seen other implementations of the Luhn algorithm on Rosetta Code and elsewhere (archived).

Those implementations work very well, but I am confused about why they can use an array to do the work. The array they use seems to have no relation with Luhn algorithm, and I can't see how they achieve the steps described on Wikipedia.

Why are they using arrays? What is the significance of them, and how are they used to implement the algorithm as described by Wikipedia?

Scintillate answered 7/9, 2012 at 2:12 Comment(2)
Your question is a bit unclear. Can you explain what you're asking again?Matz
Best Solution here plnkr.co/edit/34aR8NUpaKRCYpgnfUbK?p=preview with all test cases passed according to paypalobjects.com/en_US/vhelp/paypalmanager_help/…Mullah
D
10

the array [0,1,2,3,4,-4,-3,-2,-1,0] is used as a look up array for finding the difference between a number in 0-9 and the digit sum of 2 times its value. For example, for number 8, the difference between 8 and (2*8) = 16 -> 1+6 = 7 is 7-8 = -1.

Here is graphical presentation, where {n} stand for sum of digit of n

[{0*2}-0, {1*2}-1, {2*2}-2, {3*2}-3, {4*2}-4, {5*2}-5, {6*2}-6, {7*2}-7....]
   |       |        |         |        |        |       |         |  
[  0  ,    1    ,   2    ,    3  ,     4   ,   -4  ,   -3   ,    -2  ....] 

The algorithm you listed just sum over all the digit and for each even spot digit, look up the the difference using the array, and apply it to the total sum.

Danu answered 7/9, 2012 at 2:44 Comment(0)
B
16

Unfortunately none of the codes above worked for me. But I found on GitHub a working solution

// takes the form field value and returns true on valid number
function valid_credit_card(value) {
// accept only digits, dashes or spaces
    if (/[^0-9-\s]+/.test(value)) return false;

// The Luhn Algorithm. It's so pretty.
    var nCheck = 0, nDigit = 0, bEven = false;
    value = value.replace(/\D/g, "");

    for (var n = value.length - 1; n >= 0; n--) {
        var cDigit = value.charAt(n),
            nDigit = parseInt(cDigit, 10);

        if (bEven) {
            if ((nDigit *= 2) > 9) nDigit -= 9;
        }

        nCheck += nDigit;
        bEven = !bEven;
    }

    return (nCheck % 10) == 0;
}
Baryta answered 29/11, 2013 at 8:53 Comment(4)
pretty good but needs if (!value) return false; at the top, otherwise it returns true for an empty string or 0Biel
or you take care that you dont pass wrong values... :)Baryta
Is there any npm package or something else into TypeScript? as we can not add radix parameter to parseInt() method.Sundsvall
valid_credit_card('') or valid_credit_card('2222222') is returning trueOstracon
D
10

the array [0,1,2,3,4,-4,-3,-2,-1,0] is used as a look up array for finding the difference between a number in 0-9 and the digit sum of 2 times its value. For example, for number 8, the difference between 8 and (2*8) = 16 -> 1+6 = 7 is 7-8 = -1.

Here is graphical presentation, where {n} stand for sum of digit of n

[{0*2}-0, {1*2}-1, {2*2}-2, {3*2}-3, {4*2}-4, {5*2}-5, {6*2}-6, {7*2}-7....]
   |       |        |         |        |        |       |         |  
[  0  ,    1    ,   2    ,    3  ,     4   ,   -4  ,   -3   ,    -2  ....] 

The algorithm you listed just sum over all the digit and for each even spot digit, look up the the difference using the array, and apply it to the total sum.

Danu answered 7/9, 2012 at 2:44 Comment(0)
W
7

Compact Luhn validator:

var luhn_validate = function(imei){
    return !/^\d+$/.test(imei) || (imei.split('').reduce(function(sum, d, n){ 
            return sum + parseInt(((n + imei.length) %2)? d: [0,2,4,6,8,1,3,5,7,9][d]);
        }, 0)) % 10 == 0;
};

Works fine for both CC and IMEI numbers. Fiddle: http://jsfiddle.net/8VqpN/

Warty answered 26/6, 2013 at 13:4 Comment(6)
Beware: This implementation still doesn't work with some cards taken from Paypal's list of valid test credit cards: paypalobjects.com/en_US/vhelp/paypalmanager_help/…Reasoning
@JonathanDumaine, thanks for your bug report! A small fix was made, and the function is finally 100% working.Warty
Thanks, @zyklus, for the fix. Just something to be aware of, your fix returns true for empty strings. It's an easy fix, I know, but something people should be aware of before implementing your solution.Salamander
@PaulMcLean -- Thanks, I just copied the first part from kolypto's answer without really paying attention to it. Should be return /^\d+$/.test( imei ) && ( imei.split( '' ).reverse().reduce( function( sum, d, n ){ return +sum + ( ( n%2 ) ? [ 0,2,4,6,8,1,3,5,7,9 ][ +d ] : +d ); }, 0) ) % 10 == 0; (deleted old comment just in case)Globulin
Didn't work for valid 9 digit numbers I tried. It always returned true.Nomi
@TravisL.Riffle Implementation was completely wrong with the "fix" I updated it with a correct one @Warty In your test 76009244561 is not a valid luhn and should not pass the correct luhn is 76009244567Reactance
W
4

Lookup tables or arrays can simplify algorithm implementations - save many lines of code - and with that increase performance... if the calculation of the lookup index is simple - or simpler - and the array's memory footprint is affordable.

On the other hand, understanding how the particular lookup array or data structure came to be can at times be quite difficult, because the related algorithm implementation may look - at first sight - quite different from the original algorithm specification or description.

Indication to use lookup tables are number oriented algorithms with simple arithmetics, simple comparisons, and equally structured repetition patterns - and of course - of quite finite value sets.

The many answers in this thread go for different lookup tables and with that for different algorithms to implement the very same Luhn algorithm. Most implementations use the lookup array to avoid the cumbersome figuring out of the value for doubled digits:

var   luhnArr   =   [0,   2,   4,   6,   8,   1,   3,   5,   7,   9];
//
//                   ^    ^    ^    ^    ^    ^    ^    ^    ^    ^
//                   |    |    |    |    |    |    |    |    |    |
//
// - d-igit=index:   0    1    2    3    4    5    6    7    8    9
// - 1st 
//    calculation: 2*0  2*2  2*2  2*3  2*4  2*5  2*6  2*7  2*8  2*9 
// - intermeduate
//          value: = 0  = 2  = 4  = 6  = 8  =10  =12  =14  =16  =18
// - 2nd
//    calculation:                          1+0  1+2  1+4  1+6  1+8
//
// - final value:    0    2    4    6    8   =1   =3   =5   =7   =9
//       
var luhnFinalValue = luhnArray[d]; // d is numeric value of digit to double

An equal implementation for getting the luhnFinalValue looks like this:

var luhnIntermediateValue = d * 2; // d is numeric value of digit to double
var luhnFinalValue = (luhnIntermediateValue < 10)
        ? luhnIntermediateValue           // (d    ) * 2;
        : luhnIntermediateValue - 10 + 1; // (d - 5) * 2 + 1;

Which - with the comments in above true and false terms - is of course simplified:

var luhnFinalValue = (d < 5) ? d : (d - 5) * 2 + 1;

Now I'm not sure if I 'saved' anything at all... ;-) especially thanks the value-formed or short form of if-then-else. Without it, the code may look like this - with 'orderly' blocks and embedded in the next higher context layer of the algorithm and therefore luhnValue:

var luhnValue; // card number is valid when luhn values for each digit modulo 10 is 0

if (even) { // even as n-th digit from the the end of the string of digits
    luhnValue = d;
} else { // doubled digits
    if (d < 5) {
        luhnValue = d * 2;
    } else {
        lunnValue = (d - 5) * 2 + 1;
    }
}

Or:

var luhnValue = (even) ? d : (d < 5) ? d * 2 : (d - 5) * 2 + 1;

Btw, with modern, optimizing interpreters and (just in time) compilers, the difference is only in the source code and matters only for readability.

Having come that far with explanation - and 'justification' - of the use of lookup tables and comparison to straight forward coding, the lookup table looks now a bit overkill to me. The algorithm without is now quite easy to finish - and it looks pretty compact too:

function luhnValid(cardNo) { // cardNo as a string w/ digits only
    var sum = 0, even = false;
    cardNo.split("").reverse().forEach(function(dstr){ d = parseInt(dstr);
        sum += ((even = !even) ? d : (d < 5) ? d * 2 : (d - 5) * 2 + 1);
      });
    return (sum % 10 == 0);
  }

What strikes me after going through the explanation exercise is that the initially most enticing implementation - the one using reduce() from @kalypto - just lost totally its luster for me... not only because it is faulty on several levels, but more so because it shows that bells and whistles may not always 'ring the victory bell'. But thank you, @kalypto, it made me actually use - and understand - reduce():

function luhnValid2(cardNo) { // cardNo as a string w/ digits only
    var d = 0, e = false; // e = even = n-th digit counted from the end
    return ( cardNo.split("").reverse().reduce(
                 function(s,dstr){ d = parseInt(dstr); // reduce arg-0 - callback fnc
                     return (s + ((e = !e) ? d : [0,2,4,6,8,1,3,5,7,9][d]));
                   } // /end of callback fnc
                ,0 // reduce arg-1 - prev value for first iteration (sum)
                ) % 10 == 0
           );
  }

To be true to this thread, some more lookup table options have to be mentioned:

  • how about just adjust varues for doubled digits - as posted by @yngum
  • how about just everything with lookup tables - as posted by @Simon_Weaver - where also the values for the non-doubled digits are taken from a look up table.
  • how about just everything with just ONE lookup table - as inspired by the use of an offset as done in the extensively discussed luhnValid() function.

The code for the latter - using reduce - may look like this:

function luhnValid3(cardNo) { // cardNo as a string w/ digits only
    var d = 0, e = false; // e = even = n-th digit counted from the end
    return ( cardNo.split("").reverse().reduce(
          function(s,dstr){ d = parseInt(dstr);
              return (s + [0,1,2,3,4,5,6,7,8,9,0,2,4,6,8,1,3,5,7,9][d+((e=!e)?0:10)]);
            }
         ,0
         ) % 10 == 0
      );
  }

And for closing lunValid4() - very compact - and using just 'old fashioned' (compatible) JavaScript - with one single lookup table:

function luhnValid4(cardNo) { // cardNo as a string w/ digits only
  var s = 0, e = false, p = cardNo.length; while (p > 0) { p--;
    s += "01234567890246813579".charAt(cardNo.charAt(p)*1 + ((e=!e)?0:10)) * 1; }
  return (s % 10 == 0);
 }

Corollar: Strings can be looked at as lookup tables of characters... ;-)

A perfect example of a nice lookup table application is the counting of set bits in bits lists - bits set in a a (very) long 8-bit byte string in (an interpreted) high-level language (where any bit operations are quite expensive). The lookup table has 256 entries. Each entry contains the number of bits set in an unsigned 8-bit integer equal to the index of the entry. Iterating through the string and taking the unsigned 8-bit byte equal value to access the number of bits for that byte from the lookup table. Even for low-level language - such as assembler / machine code - the lookup table is the way to go... especially in an environment, where the microcode (instruction) can handle multiple bytes up to 256 or more in an (single CISC) instruction.

Some notes:

  • numberString * 1 and parseInt(numberStr) do about the same.
  • there are some superfluous indentations, parenthesis,etc... supporting my brain in getting the semantics quicker... but some that I wanted to leave out, are actually required... when it comes to arithmetic operations with short-form, value-if-then-else expressions as terms.
  • some formatting may look new to you; for examples, I use the continuation comma with the continuation on the same line as the continuation, and I 'close' things - half a tab - indented to the 'opening' item.
  • All formatting is all done for the human, not the computer... 'it' does care less.

Whomever answered 3/9, 2014 at 9:59 Comment(1)
luhnValid4 is the most compatible for IE, and I love the lookup table (fast). But for those in 2020 who don't care about IE6 or 7 yet still want to support IE8 (ships with Windows 7), here is a prettier and faster lookup: '01234567890246813579'[+card[p]+((e=!e)?0:10)]*1 Calls to charAt are only good for IE6/7 :)Strontium
S
3

A very fast and elegant implementation of the Luhn algorithm following:

const isLuhnValid = function luhn(array) {
      return function (number) {
        let len = number ? number.length : 0,
          bit = 1,
          sum = 0;

        while (len--) {
          sum += !(bit ^= 1) ? parseInt(number[len], 10) : array[number[len]];
        }

        return sum % 10 === 0 && sum > 0;
      };
    }([0, 2, 4, 6, 8, 1, 3, 5, 7, 9]);

console.log(isLuhnValid("4112344112344113".split(""))); // true
console.log(isLuhnValid("4112344112344114".split(""))); // false

On my dedicated git repository you can grab it and retrieve more info (like benchmarks link and full unit tests for ~50 browsers and some node.js versions).

Or you can simply install it via bower or npm. It works both on browsers and/or node.

bower install luhn-alg
npm install luhn-alg
Strophic answered 16/6, 2015 at 12:53 Comment(0)
B
1

If you want to calculate the checksum, this code from this page is very concise and in my random tests seems to work.

NOTE: the verification algorithmns on this page do NOT all work.

// Javascript
String.prototype.luhnGet = function()
{
    var luhnArr = [[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8,1,3,5,7,9]], sum = 0;
    this.replace(/\D+/g,"").replace(/[\d]/g, function(c, p, o){
        sum += luhnArr[ (o.length-p)&1 ][ parseInt(c,10) ]
    });
    return this + ((10 - sum%10)%10);
};

alert("54511187504546384725".luhnGet());​

Here's my findings for C#

Blossom answered 13/9, 2012 at 0:31 Comment(1)
I trust that that code works correctly, but the question was asking why arrays can be used in the validation and how the values in the arrays relate to the algorithm. Perhaps you could edit your answer to include the reason why you're using an array and how it works?Real
C
1
function luhnCheck(value) {
  return 0 === (value.replace(/\D/g, '').split('').reverse().map(function(d, i) {
    return +['0123456789','0246813579'][i % 2][+d];
  }).reduce(function(p, n) {
    return p + n;
  }) % 10);
}

Update: Here's a smaller version w/o string constants:

function luhnCheck(value) {
  return !(value.replace(/\D/g, '').split('').reverse().reduce(function(a, d, i) {
    return a + d * (i % 2 ? 2.2 : 1) | 0;
  }, 0) % 10);
}

note the use of 2.2 here is to make doubling d roll over with an extra 1 when doubling 5 to 9.

Cambium answered 14/2, 2015 at 2:26 Comment(1)
Why 2.2 instead of 2?Milkman
W
0

Code is the following:

var LuhnCheck = (function()
{
    var luhnArr = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9];
    return function(str)
    {
        var counter = 0;
        var incNum;
        var odd = false;
        var temp = String(str).replace(/[^\d]/g, "");
        if ( temp.length == 0)
            return false;
        for (var i = temp.length-1; i >= 0; --i)
        {
            incNum = parseInt(temp.charAt(i), 10);
            counter += (odd = !odd)? incNum : luhnArr[incNum];
        }
        return (counter%10 == 0);
    }
})();

The variable counter is the sum of all the digit in odd positions, plus the double of the digits in even positions, when the double exceeds 10 we add the two numbers that make it (ex: 6 * 2 -> 12 -> 1 + 2 = 3)

The Array you are asking about is the result of all the possible doubles

var luhnArr = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9];

  • 0 * 2 = 0 --> 0
  • 1 * 2 = 2 --> 2
  • 2 * 2 = 4 --> 4
  • 3 * 2 = 6 --> 6
  • 4 * 2 = 8 --> 8
  • 5 * 2 = 10 --> 1+0 --> 1
  • 6 * 2 = 12 --> 1+2 --> 3
  • 7 * 2 = 14 --> 1+4 --> 5
  • 8 * 2 = 16 --> 1+6 --> 7
  • 9 * 2 = 18 --> 1+8 --> 9

So for example

luhnArr[3] --> 6 (6 is in 3rd position of the array, and also 3 * 2 = 6)
luhnArr[7] --> 5 (5 is in 7th position of the array, and also 7 * 2 = 14 -> 5 )
Wahl answered 7/9, 2012 at 2:48 Comment(0)
G
0

Another alternative:

function luhn(digits) {
    return /^\d+$/.test(digits) && !(digits.split("").reverse().map(function(checkDigit, i) { 
        checkDigit = parseInt(checkDigit, 10);
        return i % 2 == 0
            ? checkDigit
            : (checkDigit *= 2) > 9 ? checkDigit - 9 : checkDigit;
    }).reduce(function(previousValue, currentValue) {
        return previousValue + currentValue;
    }) % 10);
}
Gorgonian answered 8/11, 2016 at 16:17 Comment(0)
M
0

Alternative ;) Simple and Best

  <script>
      // takes the form field value and returns true on valid number
    function valid_credit_card(value) {
      // accept only digits, dashes or spaces
        if (/[^0-9-\s]+/.test(value)) return false;

        // The Luhn Algorithm. It's so pretty.
        var nCheck = 0, nDigit = 0, bEven = false;
        value = value.replace(/\D/g, "");

        for (var n = value.length - 1; n >= 0; n--) {
            var cDigit = value.charAt(n),
                  nDigit = parseInt(cDigit, 10);

            if (bEven) {
                if ((nDigit *= 2) > 9) nDigit -= 9;
            }

            nCheck += nDigit;
            bEven = !bEven;
        }

        return (nCheck % 10) == 0;
    }

    console.log(valid_credit_card("5610591081018250"),"valid_credit_card Validation");
  </script>

Best Solution here

with all test cases passed according to

and the credit goes to

Mullah answered 11/4, 2017 at 7:16 Comment(0)
S
0
const LuhnCheckCard = (number) => {
  if (/[^0-9-\s]+/.test(number) || number.length === 0)
    return false;

  return ((number.split("").map(Number).reduce((prev, digit, i) => {
    (!(( i & 1 ) ^ number.length)) && (digit *= 2);    
    (digit > 9) && (digit -= 9);    
    return prev + digit;
  }, 0) % 10) === 0);
}

console.log(LuhnCheckCard("4532015112830366")); // true
console.log(LuhnCheckCard("gdsgdsgdsg")); // false
Spume answered 19/5, 2017 at 15:47 Comment(0)
E
0

I worked out the following solution after I submitted a much worse one for a test..

function valid(number){
    var splitNumber = parseInt(number.toString().split(""));
    var totalEvenValue = 0;
    var totalOddValue = 0;
    for(var i = 0; i < splitNumber.length; i++){
        if(i % 2 === 0){
            if(splitNumber[i] * 2 >= 10){
                totalEvenValue += splitNumber[i] * 2 - 9;
            } else {
                totalEvenValue += splitNumber[i] * 2;
            }
        }else {
            totalOddValue += splitNumber[i];
        }
    }
    return ((totalEvenValue + totalOddValue) %10 === 0)
}
console.log(valid(41111111111111111));
Effluvium answered 25/1, 2019 at 2:1 Comment(0)
U
0

I recently wrote a solution using Javascript, I leave the code here for anyone who can help:

// checksum with Luhn Algorithm
const luhn_checksum = function(strIn) {
    const len = strIn.length;
    let sum = 0
    for (let i = 0; i<10; i += 1) {
      let factor = (i % 2 === 1) ? 2: 1
      const v = parseInt(strIn.charAt(i), 10) * factor
      sum += (v>9) ? (1 + v % 10) : v
    }
    return (sum * 9) % 10
}

// teste exampple on wikipedia:
// https://en.wikipedia.org/wiki/Luhn_algorithm
const strIn = "7992739871"

// The checksum of "7992739871" is 3
console.log(luhn_checksum(strIn))

If you understand this code above, you will have no problem converting it to any other language.

For example in python:

def nss_checksum(nss):
  suma = 0
  for i in range(10):
    factor = 2 if (i % 2 == 1) else 1
    v = int(nss[i]) * factor
    suma += (1 + v % 10) if (v >9) else v
  return (suma * 9) % 10

For more info, check this:

My Code(En español):

Ushas answered 27/6, 2022 at 18:59 Comment(0)
F
-1
def validate_credit_card_number(card_number):
if(len(str(card_number))==16):
    group1 = []
    group1_double = []
    after_group_double = []
    group1_sum = 0
    group2_sum = 0
    group2 = []
    total_final_sum = 0
    s = str(card_number)
    list1 = [int(i) for i in list(s)]
    for i in range(14, -1, -2):
        group1.append(list1[i])
    for x in group1:
        b = 0
        b = x * 2
        group1_double.append(b)
    for j in group1_double:
        if(j > 9):
            sum_of_digits = 0
            alias = str(j)
            temp1 = alias[0]
            temp2 = alias[1]
            sum_of_digits = int(temp1) + int(temp2)
            after_group_double.append(sum_of_digits)
        else:
            after_group_double.append(j)
    for i in after_group_double:
        group1_sum += i
    for i in range(15, -1, -2):
        group2.append(list1[i])
    for i in group2:
        group2_sum += i

    total_final_sum = group1_sum + group2_sum

    if(total_final_sum%10==0):
        return True
    else:
        return False

card_number= 1456734512345698 #4539869650133101  #1456734512345698 # #5239512608615007
result=validate_credit_card_number(card_number)
if(result):
    print("credit card number is valid")
else:
    print("credit card number is invalid")
Flash answered 20/3, 2019 at 8:32 Comment(1)
This doesn't look like JavaScript. You should also include some explanation in your answer to demonstrate how it solves the problemIpa

© 2022 - 2025 — McMap. All rights reserved.