I need to validate an input string using checksum for alphanumerics

1.8k views Asked by At

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

2

There are 2 answers

0
RobG On BEST ANSWER

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.

1
traktor On

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"));