I wrote a JavaScript function for validating bank account numbers in IBAN format for one of my big banking customers. This is the first version of the code. After a complete code review, many changes were suggested. Here's some of the code that came out of this exercise.
function isIbanValid(value) { var lengthMap = getLengthMap(); //cleanup value = value.toString().toUpperCase().replace(/\s/g, '').replace(/[-]/g, ''); //check if alphanumeric if (!/^[a-zA-Z0-9]+$/.test(value)) return false; //extract countrycode var countryCode = value.substring(0, 2); //check if letter if (!/([a-z]+[\s\-]?)*/i.test(countryCode)) return false; //check string length if (value.length != lengthMap[countryCode]) return false; value = value.concat(value.substring(0, 4)).substring(4); value = value.replace(countryCode, countryCodeToStringValue(countryCode)); return modulo(value, 97) == 1; function countryCodeToStringValue(countryCode) { return "".concat(((countryCode.charCodeAt(0)) - 55).toString() + (countryCode.charCodeAt(1) - 55).toString()); } function modulo(divident, divisor) { var quantization = 12; while (quantization < divident.length) { var part = divident.substring(0, quantization); divident = (part % divisor) + divident.substring(quantization); } return divident % divisor; } function getLengthMap() { var lengthMap = {}; lengthMap["AD"] = 24; lengthMap["AT"] = 20; lengthMap["AZ"] = 28; lengthMap["BH"] = 22; lengthMap["BE"] = 16; lengthMap["BA"] = 20; lengthMap["BR"] = 29; lengthMap["BG"] = 22; lengthMap["CR"] = 21; lengthMap["HR"] = 21; lengthMap["CY"] = 28; lengthMap["CZ"] = 24; lengthMap["DK"] = 18; lengthMap["DO"] = 28; lengthMap["EE"] = 20; lengthMap["FO"] = 18; lengthMap["FI"] = 18; lengthMap["FR"] = 27; lengthMap["DE"] = 22; lengthMap["GR"] = 27; lengthMap["GI"] = 23; lengthMap["GL"] = 18; lengthMap["GT"] = 28; lengthMap["HU"] = 28; lengthMap["IS"] = 26; lengthMap["IE"] = 22; lengthMap["IL"] = 23; lengthMap["IT"] = 27; lengthMap["JO"] = 30; lengthMap["KZ"] = 20; lengthMap["KW"] = 30; lengthMap["LV"] = 21; lengthMap["LB"] = 28; lengthMap["LI"] = 21; lengthMap["LT"] = 20; lengthMap["LU"] = 20; lengthMap["MK"] = 19; lengthMap["MT"] = 31; lengthMap["MR"] = 27; lengthMap["MU"] = 30; lengthMap["MC"] = 27; lengthMap["MD"] = 24; lengthMap["ME"] = 22; lengthMap["NL"] = 18; lengthMap["NO"] = 15; lengthMap["PK"] = 24; lengthMap["PS"] = 29; lengthMap["PL"] = 28; lengthMap["PT"] = 25; lengthMap["QA"] = 29; lengthMap["RO"] = 24; lengthMap["SM"] = 27; lengthMap["SA"] = 24; lengthMap["RS"] = 22; lengthMap["SK"] = 24; lengthMap["SI"] = 19; lengthMap["ES"] = 24; lengthMap["SE"] = 24; lengthMap["CH"] = 21; lengthMap["TN"] = 24; lengthMap["TR"] = 26; lengthMap["AE"] = 23; lengthMap["GB"] = 22; return lengthMap; } }
Someone suggested rewriting the whole thing as this:
var validIBAN = (function () { // use an IIFE // A "constant" lookup table of IBAN lengths per country // (the funky formatting is just to make it fit better in the answer here on CR) var CODE_LENGTHS = { AD: 24, AE: 23, AT: 20, AZ: 28, BA: 20, BE: 16, BG: 22, BH: 22, BR: 29, CH: 21, CR: 21, CY: 28, CZ: 24, DE: 22, DK: 18, DO: 28, EE: 20, ES: 24, FI: 18, FO: 18, FR: 27, GB: 22, GI: 23, GL: 18, GR: 27, GT: 28, HR: 21, HU: 28, IE: 22, IL: 23, IS: 26, IT: 27, JO: 30, KW: 30, KZ: 20, LB: 28, LI: 21, LT: 20, LU: 20, LV: 21, MC: 27, MD: 24, ME: 22, MK: 19, MR: 27, MT: 31, MU: 30, NL: 18, NO: 15, PK: 24, PL: 28, PS: 29, PT: 25, QA: 29, RO: 24, RS: 22, SA: 24, SE: 24, SI: 19, SK: 24, SM: 27, TN: 24, TR: 26 }; // piece-wise mod97 using 9 digit "chunks", as per Wikipedia's example: // http://en.wikipedia.org/wiki/International_Bank_Account_Number#Modulo_operation_on_IBAN function mod97(string) { var checksum = string.slice(0, 2), fragment; for(var offset = 2 ; offset < string.length ; offset += 7) { fragment = String(checksum) + string.substring(offset, offset + 7); checksum = parseInt(fragment, 10) % 97; } return checksum; } // return a function that does the actual work return function (input) { var iban = String(input).toUpperCase().replace(/[^A-Z0-9]/g, ''), // keep only alphanumeric characters code = iban.match(/^([A-Z]{2})(\d{2})([A-Z\d]+)$/), // match and capture (1) the country code, (2) the check digits, and (3) the rest digits; // check syntax and length if(!code || iban.length !== CODE_LENGTHS[code[1]]) { return false; } // rearrange country code and check digits, and convert chars to ints digits = (code[3] + code[1] + code[2]).replace(/[A-Z]/g, function (letter) { return letter.charCodeAt(0) - 55; }); // final check return mod97(digits) === 1; }; }());
Somebody else suggested making better mod97 code
static mod97(acc, digit, index) { return index == 9 || (index > 9 && index % 7 == 0) ? (Number(acc + digit) % 97).toString() : acc + digit }
0 Comments
|
AuthorWrite something about yourself. No need to be fancy, just an overview. ArchivesCategories
All
|