From 7c39f970f4c3753e5f1fdfd2975cfa0b9d19de31 Mon Sep 17 00:00:00 2001 From: "Bruce A. MacNaughton" Date: Fri, 19 Mar 2021 20:40:49 -0700 Subject: [PATCH 1/3] optimize isISIN speed + gc --- src/lib/isISIN.js | 51 +++++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/lib/isISIN.js b/src/lib/isISIN.js index 4576b684c..cc8f05cac 100644 --- a/src/lib/isISIN.js +++ b/src/lib/isISIN.js @@ -9,27 +9,44 @@ export default function isISIN(str) { return false; } - const checksumStr = str.replace(/[A-Z]/g, character => (parseInt(character, 36))); - + let double = true; let sum = 0; - let digit; - let tmpNum; - let shouldDouble = true; - for (let i = checksumStr.length - 2; i >= 0; i--) { - digit = checksumStr.substring(i, (i + 1)); - tmpNum = parseInt(digit, 10); - if (shouldDouble) { - tmpNum *= 2; - if (tmpNum >= 10) { - sum += tmpNum + 1; - } else { - sum += tmpNum; + // convert values + for (let i = str.length - 2; i >= 0; i--) { + if (str[i] >= 'A' && str[i] <= 'Z') { + const value = str[i].charCodeAt(0) - 55; + const lo = value % 10; + const hi = Math.trunc(value / 10); + // letters have two digits, so handle the low order + // and high order digits separately. + for (const digit of [lo, hi]) { + if (double) { + if (digit >= 5) { + sum += 1 + ((digit - 5) * 2); + } else { + sum += digit * 2; + } + } else { + sum += digit; + } + double = !double; } } else { - sum += tmpNum; + const digit = str[i].charCodeAt(0) - '0'.charCodeAt(0); + if (double) { + if (digit >= 5) { + sum += 1 + ((digit - 5) * 2); + } else { + sum += digit * 2; + } + } else { + sum += digit; + } + double = !double; } - shouldDouble = !shouldDouble; } - return parseInt(str.substr(str.length - 1), 10) === (10000 - sum) % 10; + const check = (Math.trunc(((sum + 9) / 10)) * 10) - sum; + + return +str[str.length - 1] === check; } From 0891e685680dc1fa5eaa1067880f6ffc9e86ce1b Mon Sep 17 00:00:00 2001 From: "Bruce A. MacNaughton" Date: Fri, 19 Mar 2021 20:41:04 -0700 Subject: [PATCH 2/3] add aapl ISIN --- test/validators.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/validators.js b/test/validators.js index 34b6474e8..d633fc7df 100644 --- a/test/validators.js +++ b/test/validators.js @@ -4683,6 +4683,7 @@ describe('Validators', () => { 'GB0001411924', 'DE000WCH8881', 'PLLWBGD00016', + 'US0378331005', ], invalid: [ 'DE000BAY0018', From c31da98b244eab651a1589ac873313cb1bccc5f7 Mon Sep 17 00:00:00 2001 From: "Bruce A. MacNaughton" Date: Sun, 21 Mar 2021 10:18:15 -0700 Subject: [PATCH 3/3] comment and reference --- src/lib/isISIN.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/isISIN.js b/src/lib/isISIN.js index cc8f05cac..a6f6fa645 100644 --- a/src/lib/isISIN.js +++ b/src/lib/isISIN.js @@ -2,6 +2,12 @@ import assertString from './util/assertString'; const isin = /^[A-Z]{2}[0-9A-Z]{9}[0-9]$/; +// this link details how the check digit is calculated: +// https://www.isin.org/isin-format/. it is a little bit +// odd in that it works with digits, not numbers. in order +// to make only one pass through the ISIN characters, the +// each alpha character is handled as 2 characters within +// the loop. export default function isISIN(str) { assertString(str);