ISO 6346 – Shipping Container Codes

Fun with Algorithms

Estimated Reading Time: 15 minutes
Difficulty: Intermediate

This post is purely illustrative and showcases a few implementations that achieve the same goal. Feel free to use any of them in your implementation.

I often spend time researching and implementing algorithms in multiple languages as a way to keep my programming skillset sharp, learn a new language and its syntactical sugar, and to have the algorithms at my disposal when and if I need them.

This article references
Container Container ISO 6346
Wikipedia ISO 6346

ISO 6346 is an international standard that describes the identification of a shipping container. The standard is maintained by the BIC (International Container Bureau) and covers the serial number, owner, country code, and size of any given shipping container.

An example of a compliant ISO 6346 Code is CSQU3054383

Owner CodeCategory IdentifierSerial NumberCheck Digit
CSQU3054383
[A-Z]{3}[UJRZ]\d{6}\d
A container owned by Hapag-Lloyd AG.


It is relatively straightforward to validate via Regex as shown above. However, the fun algorithm portion that validates a shipping container code is as follows.

Calculation 1

An equivalent numerical value is assigned to each letter of the alphabet, beginning with 10 for the letter A (11 and multiples thereof are omitted) for Owner Code and Category Identifier.

ABCDEFGHIJKLM
10121314151617181920212324
NOPQRSTUVWXYZ
25262728293031323435363738

Calculation 2

Each of the numbers calculated in step 1 is multiplied by 2position, where position is the exponent to base 2. Position starts at 0, from left to right.

CSQU305438
13302832305438
1248163264128256512
13601122564803205127684096

Calculation 3

  1. Sum up all results of Step above = 6185
  2. Divide them by 11 = 562.272..
  3. Round the result down towards zero i.e. make the result a whole number (integer) = 562
  4. Multiply the integer value by 11 = 6182
  5. Subtract the result of (4) from the result of (1): This is the check digit = 3

Calculation 1 Implementations

While there are multiple calculations as outlined above, the Calculation 1 implementation is the most interesting. The goal for me was to retrieve the number mapped to an A-Z character without the use of if statements.

Implementation 1 (Dictionary)

This seems simple enough, create a dictionary/object that represents these values. This will be the most common implementation seen on the web.

const alphaReductionDictionary: { [character: string]: number } = {
        "A": 10, "B": 12, "C": 13, "D": 14, "E": 15, "F": 16, "G": 17, "H": 18, "I": 19, "J": 20, "K": 21, "L": 23, "M": 24,
        "N": 25, "O": 26, "P": 27, "Q": 28, "R": 29, "S": 30, "T": 31, "U": 32, "V": 34, "W": 35, "X": 36, "Y": 37, "Z": 38
 }

Implementation 2 (ASCII Character Code w/ Math)

However, I wanted to see if this can be solved via ASCII char code and math alone, as this seems to be a common theme in alphanumeric codes: omit multiples of n

const charCodeReduced = shippingContainerChar.charCodeAt(0) - 55; // this reduces ASCII A to 10, as per algorithm
const charCodeRemainder = charCodeReduced % 11; // identifies occurrences of multiples of 11, and the position of items that arent
const weight = Math.trunc((charCodeReduced - charCodeRemainder) / 10); // generates a number that indicates any addition to charCode needed to skip multiples of 11
return Math.trunc((charCodeReduced + weight) / 11) + charCodeReduced; // generates an integer as per the ISO 6346 specification

Implementation 3 (Most Elegant)

While I have two solid implementations, I realized that position played a part in the calculation, and with A starting at 10, 0-9 fit nicely in front of it. I thought I was original in this concept however, it seems that this is a valid way of solving this problem as well. It took some digging, but it was found on bitcoinwiki.

const characterIndexMap: string = "0123456789A?BCDEFGHIJK?LMNOPQRSTU?VWXYZ";
return Number(characterIndexMap.indexOf(shippingContainerChar, 1));

With these implementations in place, creating a function that completes the algorithm is easy. Note, the full implementation allows the implementations above to be injected into the calculation process.

export const isISO6346Compliant = function (containerNumber: string, getCharValue: (c: string) => number): boolean {
	// fail fast if any parameter is null or undefined
    if (!containerNumber || /^\s+$/.test(containerNumber) || !getCharValue) return false;
    
	// validate for ISO 6346 compliant container number
	const isoPattern: RegExp = /^[A-Z]{3}[UJRZ]\d{6}\d$/;
    
	// fail fast if the containerNumber does not meet ISO 6346 standards
    if (!isoPattern.test(containerNumber)) return false;
    
    // get check digit (last character of containerNumber)
    const checkDigit: number = Number(containerNumber[containerNumber.length - 1]);

    /*
     * Calculation Step 3a
     *
     * calculate sum of container number chars multiplied by exponent to base 2,
     * the last character (check digit) is omitted from calculation as per spec
     */
    let calcSumExpB2: number = 0;
    for (let i = 0; i < containerNumber.length - 1; i++) {
        let extractedValue: number = 0;
        const contNumChar: string = containerNumber[i];
        /*
         * the first 4 characters are alphabetical, they need to be converted with
         * multiples of 11 omitted
         */
        extractedValue = (i < 4)? getCharValue(contNumChar) : Number(contNumChar);
        // multiply the extracted value by exponent to base 2 using the position of the char
        calcSumExpB2 += extractedValue * Math.pow(2, i);
    }

    // Calculation Step 3b: Division by 11
    let stepCalc = calcSumExpB2 / 11;

    // Calculation Step 3c: Erase decimal digits
    stepCalc = Math.trunc(stepCalc);

    // Calculation Step 3d: Multiply by 11
    stepCalc *= 11;

    // Calculation Step 3e: a) - d) === CheckDigit
    return calcSumExpB2 - stepCalc === checkDigit;
}

There it is!

This exercise was a lot of fun and showcased various ways of implementing an algorithm. The full source code for this exercise can be found here.

Leave a comment