Skip to content

Instantly share code, notes, and snippets.

@kirkouimet
Last active May 5, 2023 16:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kirkouimet/ad233fe56f8af7af4c9a7c738cf05c7f to your computer and use it in GitHub Desktop.
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
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