1

I have the following function that validates a digits input consisted of only numbers based on Luhn Algorithm:

function isCheckdigitCorrect(value) {
// accept only digits, dashes or spaces
  if (/[^0-9-\s]+/.test(value)) return false;

  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;
}

Is there anyway that I can validate also alphanumerics, so let's suppose I have a valid ID: AC813(6) , () is the checksum. So is there a way that I can prevent users having to type mistakenly AF813(6) so this would tell user incorrect ID.

I appreciate your help

pranvera hoti
  • 107
  • 3
  • 15
  • You have to provide the algorithm for converting letters and digits to the checksum. It will be different to the Luhn algorithm. – RobG Apr 27 '17 at 23:16
  • I would read: https://en.wikipedia.org/wiki/Luhn_mod_N_algorithm – traktor Apr 27 '17 at 23:21
  • @RobG do u know any algorithm or even much more helpful any sample of it written in any programming language so then I can adapt it for my needs? Or is there anyway I can modify the current code to support alphanumeric validation? – pranvera hoti Apr 27 '17 at 23:22
  • I am thinking of removing fitst if condition and initially declare an array of object like this: var lettersToNumbers = [{'A':0,'B':1,'C':2....}]; which converts each letter to number and so every letter in this case is some number. So in this case I dont think I even need to change the rest of the code at all... Do you think this will work @RobG – pranvera hoti Apr 27 '17 at 23:38
  • @Traktor53 what do you think of my last comment? – pranvera hoti Apr 27 '17 at 23:41
  • @pranverahoti—yes, something like that, but just an object, it doesn't need to be inside an array. – RobG Apr 28 '17 at 00:34

2 Answers2

3

Substituting digits for alphabetic characters to calculate a checksum severely reduces the robustness of the check, and the simplest suggestion I can come up with is to use the Luhn mod N algorithm described on Wikipedia.

Translating the algorithm into JavaScipt was relatively straight forward: the following is not my code but a translation from the wiki article - so I won't pretend it is optimal. It is intended to work with strings of case insensitive ASCII alphabetic characters and decimal digits. For documentation see the wiki.

// based on https://en.wikipedia.org/wiki/Luhn_mod_N_algorithm

var charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
function NumberOfValidInputCharacters () { return charset.length; }
function CodePointFromCharacter(character) { return charset.indexOf(character)};
function CharacterFromCodePoint( codePoint) { return charset[codePoint]};

function GenerateCheckCharacter (input) {

    var factor = 2;
    var sum = 0;
    var n = NumberOfValidInputCharacters();
    input = input.toUpperCase();

    // Starting from the right and working leftwards is easier since 
    // the initial "factor" will always be "2" 
    for (var i = input.length - 1; i >= 0; i--) {
        var codePoint = CodePointFromCharacter(input[i]);
        if( codePoint < 0) {
            return "";
        }

        var addend = factor * codePoint;

        // Alternate the "factor" that each "codePoint" is multiplied by
        factor = (factor == 2) ? 1 : 2;

        // Sum the digits of the "addend" as expressed in base "n"
        addend = Math.floor(addend / n) + (addend % n);
        sum += addend;
    }

    // Calculate the number that must be added to the "sum" 
    // to make it divisible by "n"
    var remainder = sum % n;
    var checkCodePoint = (n - remainder) % n;

    return CharacterFromCodePoint(checkCodePoint);
}

function ValidateCheckCharacter(input) {

    var factor = 1;
    var sum = 0;
    var n = NumberOfValidInputCharacters();
    input = input.toUpperCase();

    // Starting from the right, work leftwards
    // Now, the initial "factor" will always be "1" 
    // since the last character is the check character
    for (var i = input.length - 1; i >= 0; i--) {
        var codePoint = CodePointFromCharacter(input[i]);
        if( codePoint < 0) {
            return false;
        }

        var addend = factor * codePoint;

        // Alternate the "factor" that each "codePoint" is multiplied by
        factor = (factor == 2) ? 1 : 2;

        // Sum the digits of the "addend" as expressed in base "n"
        addend = Math.floor(addend / n) + (addend % n);
        sum += addend;
    }

    var remainder = sum % n;

    return (remainder == 0);
}

// quick test:
console.log ("check character for 'abcde234': %s",
     GenerateCheckCharacter("abcde234"));
console.log( "validate  'abcde2349' : %s " ,
     ValidateCheckCharacter( "abcde2349"));
console.log( "validate  'abcde234X' : %s" ,
     ValidateCheckCharacter( "abcde234X"));
traktor
  • 17,588
  • 4
  • 32
  • 53
  • This is great, but i had to mark as true the other answer as I dont have to change the whole previous code I wrote. Thanx anyhow, If I am able to answer this too, I would. – pranvera hoti Apr 28 '17 at 08:23
1

If you just want to do the Luhn algorithm with letters replacing some of the numbers, then include an additional step to convert letters to numbers within your function.

So if you wanted to allow say A, B, C, D that convert to 0, 1, 2, 3 then you could do:

function isCheckdigitCorrect(value) {

  // Letter to number mapping
  var letters = {a:'0', b:'1', c:'2', d:'3'};
  
  // Convert letters to their number equivalents, if they have one
  value = value.split('').reduce(function(s, c){
    return s += letters[c.toLowerCase()] || c;
  },'');

  // Continue as currently
  // accept only digits, dashes or spaces
  if (/[^0-9-\s]+/.test(value)) return false;

  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;
}

// In the following, A = 0 and D = 3
console.log(isCheckdigitCorrect('375767AA4D6AA21'));

You can implement other algorithms in a similar way.

RobG
  • 142,382
  • 31
  • 172
  • 209