Last active
May 5, 2023 16:30
-
-
Save kirkouimet/ad233fe56f8af7af4c9a7c738cf05c7f to your computer and use it in GitHub Desktop.
Generate an easy to communicate order identifier with a check digit with a keyspace in the trillions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class OrderIdentifierGenerator { | |
static characters = '234567CDFGHJKLMNPQRTVWXYZ'; | |
// Generate a new identifier | |
static generate() { | |
const partialIdentifier = OrderIdentifierGenerator.generatePartialIdentifier(); | |
let identifier = ( | |
partialIdentifier.slice(0, 3) + | |
"-" + | |
partialIdentifier.slice(3, 6) + | |
"-" + | |
partialIdentifier.slice(6) | |
); | |
// Add the Luhn check digit | |
identifier += OrderIdentifierGenerator.getLuhnCheckDigit(partialIdentifier); | |
// Validate | |
// console.log('identifier', identifier); | |
// console.log('isValid', OrderIdentifierGenerator.validate(identifier)); | |
return identifier; | |
} | |
// Generate a partial identifier without hyphens or check digit | |
static generatePartialIdentifier() { | |
const totalLength = 9; | |
let partialIdentifier = ''; | |
for (let index = 0; index < totalLength; index++) { | |
partialIdentifier += OrderIdentifierGenerator.getRandomCharacter(); | |
} | |
// console.log('partialIdentifier', partialIdentifier); | |
return partialIdentifier; | |
} | |
// Generate a random character from the character set | |
static getRandomCharacter() { | |
// Get a random index within the range of the character set | |
const randomIndex = Math.floor(Math.random() * OrderIdentifierGenerator.characters.length); | |
// Return the character at the random index | |
return OrderIdentifierGenerator.characters.charAt(randomIndex); | |
} | |
// Calculate the Luhn check digit for the partial identifier | |
static getLuhnCheckDigit(partialIdentifier) { | |
// Create a mapping of characters to their index values | |
const valueMapping = [...OrderIdentifierGenerator.characters].reduce(function ( | |
accumulator, | |
character, | |
index | |
) { | |
accumulator[character] = index; | |
return accumulator; | |
}, | |
{}); | |
let sum = 0; | |
let alternate = false; | |
// Iterate through the partialIdentifier characters in reverse order | |
for (let index = partialIdentifier.length - 1; index >= 0; index--) { | |
let number = valueMapping[partialIdentifier[index]]; | |
// If it's an alternate character, double its value | |
if (alternate) { | |
number = number * 2; | |
// If the doubled value is greater than the character set length, subtract the character set length and add 1 | |
if (number > OrderIdentifierGenerator.characters.length - 1) { | |
number = number - OrderIdentifierGenerator.characters.length + 1; | |
} | |
} | |
// Add the number to the sum | |
sum += number; | |
// Toggle the alternate flag for the next character | |
alternate = !alternate; | |
} | |
// Calculate the check digit index based on the sum and character set length | |
const checkDigitIndex = | |
(OrderIdentifierGenerator.characters.length - (sum % OrderIdentifierGenerator.characters.length)) % | |
OrderIdentifierGenerator.characters.length; | |
// Return the check digit character | |
return OrderIdentifierGenerator.characters[checkDigitIndex]; | |
} | |
// Validate an order identifier by checking its Luhn check digit | |
static validate(identifier) { | |
// Remove hyphens from the identifier | |
identifier = identifier.replace(/-/g, ''); | |
// Get the last character of the identifier | |
const checkDigit = identifier[identifier.length - 1]; | |
// Get the partial identifier without the check digit | |
const partialIdentifier = identifier.slice(0, -1); | |
// Calculate the Luhn check digit for the partial identifier | |
const calculatedCheckDigit = OrderIdentifierGenerator.getLuhnCheckDigit(partialIdentifier); | |
// Return whether the check digit matches the calculated check digit | |
return checkDigit === calculatedCheckDigit; | |
} | |
} | |
// Test | |
const orderIdentifier = OrderIdentifierGenerator.generate(); | |
const isValid = OrderIdentifierGenerator.validate(orderIdentifier); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment