Complete Implementation and Algorithm Analysis of Adding Ordinal Suffixes to Numbers in JavaScript

Dec 03, 2025 · Programming · 7 views · 7.8

Keywords: JavaScript | ordinal suffixes | algorithm implementation

Abstract: This article provides an in-depth exploration of various methods for adding English ordinal suffixes (st, nd, rd, th) to numbers in JavaScript. It begins by explaining the fundamental rules of ordinal suffixes, including special handling for numbers ending in 11, 12, and 13. The article then analyzes three different implementation approaches: intuitive conditional-based methods, concise array-mapping solutions, and mathematically derived one-line implementations. Each method is accompanied by complete code examples and step-by-step explanations to help developers understand the logic and performance considerations behind different implementations. The discussion also covers best practices and considerations for real-world applications, including handling negative numbers, edge cases, and balancing code readability with efficiency.

Fundamental Rules of Ordinal Suffixes

In English, adding ordinal suffixes to numbers follows specific grammatical rules. These rules apply not only to simple single-digit numbers but also to more complex cases involving tens and hundreds. The core rules can be summarized as follows:

While these rules appear straightforward, practical programming implementations require careful attention to edge cases and special scenarios.

Conditional-Based Implementation Approach

The most intuitive implementation method uses conditional statements to handle different number cases. This approach is easy to understand and maintain, making it particularly suitable for beginners and projects requiring high readability.

function ordinalSuffixOf(i) {
    let j = i % 10,
        k = i % 100;
    if (j === 1 && k !== 11) {
        return i + "st";
    }
    if (j === 2 && k !== 12) {
        return i + "nd";
    }
    if (j === 3 && k !== 13) {
        return i + "rd";
    }
    return i + "th";
}

// Test code
for (let n = 0; n <= 115; n++) {
    console.log(n + " " + ordinalSuffixOf(n));
}

The key to this implementation lies in checking both the last digit (j = i % 10) and the last two digits (k = i % 100) simultaneously. This approach correctly handles special cases like 11, 12, and 13. For example, when i = 11, j = 1 but k = 11, so it doesn't enter the first conditional branch and directly returns "11th".

Concise Array-Mapping Solution

Another common implementation approach uses arrays to map suffix rules. This method is typically more concise but requires understanding the underlying mathematical logic.

function getNumberWithOrdinal(n) {
    var s = ["th", "st", "nd", "rd"],
        v = n % 100;
    return n + (s[(v - 20) % 10] || s[v] || s[0]);
}

// Test various edge cases
[-4, -1, 0, 1, 2, 3, 4, 10, 11, 12, 13, 14, 20, 21, 22, 100, 101, 111].forEach(
    n => console.log(n + ' -> ' + getNumberWithOrdinal(n))
);

The cleverness of this algorithm lies in using JavaScript's logical OR operator (||) to handle array index out-of-bounds situations. The expression (v - 20) % 10 maps 20-29 to 0-9, 30-39 to 0-9, and so on. When this expression yields a result outside the 0-3 range, s[(v - 20) % 10] returns undefined, then tries s[v], and finally falls back to s[0] (i.e., "th").

Mathematically Derived One-Line Solution

For developers seeking maximum conciseness, a one-line solution based on mathematical formulas is available. While this approach minimizes code size, it sacrifices readability and is best suited for performance-critical applications or situations with strict code size constraints.

function nth(n) {
    return ["st", "nd", "rd"][((n + 90) % 100 - 10) % 10 - 1] || "th";
}

// Variant supporting negative numbers
function nthWithNegative(n) {
    return ["st", "nd", "rd"][(((n < 0 ? -n : n) + 90) % 100 - 10) % 10 - 1] || "th";
}

// ES6 arrow function version
const nthArrow = n => ["st", "nd", "rd"][(((n < 0 ? -n : n) + 90) % 100 - 10) % 10 - 1] || "th";

The complex mathematical expression ((n + 90) % 100 - 10) % 10 - 1 is actually a carefully designed mapping function. Its operation can be broken down into several steps:

  1. (n + 90) % 100: Maps input number n to the range 0-99
  2. - 10: Adjusts the mapping range
  3. % 10: Obtains the last digit
  4. - 1: Adjusts the result to array indices (0, 1, 2)

When the expression yields a result outside the 0-2 range, array access returns undefined, and the logical OR operator returns the default "th" suffix.

Practical Considerations in Real Applications

When selecting an appropriate implementation method, several practical factors should be considered:

Readability and Maintainability

For most projects, readability and maintainability should be the primary considerations. Although the conditional-based method involves more code, its logic is clear and easy for other developers to understand and modify. This advantage is particularly significant in team collaborations or long-term maintenance projects.

Performance Considerations

In performance-sensitive applications, the efficiency differences between implementation methods may need evaluation. Generally, conditional-based methods might be less efficient in some JavaScript engines due to branch prediction, compared to array-based approaches. However, for most application scenarios, these performance differences are negligible.

Edge Case Handling

All implementation methods must properly handle various edge cases:

Internationalization Considerations

It's important to note that the rules discussed in this article apply only to English ordinal suffixes. Other languages may have completely different rule systems. In multilingual applications, corresponding logic must be implemented for each language, or established internationalization libraries should be used.

Best Practice Recommendations

Based on the above analysis, we propose the following best practice recommendations:

  1. For most projects, the conditional-based implementation method is recommended as it achieves a good balance between readability, maintainability, and performance.
  2. If a project has strict code size constraints (e.g., frontend resource optimization), consider using array-mapping or one-line solutions.
  3. Always write comprehensive test cases covering all edge cases, including negative numbers, zero, special numbers (11, 12, 13), and large numbers.
  4. Consider encapsulating ordinal suffix generation functionality as independent utility functions or modules for easier reuse and maintenance.
  5. When designing APIs, clearly define function input-output conventions, including whether negative numbers are supported and how non-integer inputs are handled.

By understanding the principles and applicable scenarios of different implementation methods, developers can select the most suitable solution based on specific requirements, thereby writing code that is both efficient and easy to maintain.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.