最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Add st, nd, rd and th (ordinal) suffix to a number - Stack Overflow

programmeradmin17浏览0评论

I would like to dynamically generate a string of text based on a current day. So, for example, if it is day 1 then I would like my code to generate = "Its the <dynamic>1*<dynamic string>st</dynamic string>*</dynamic>".

There are 12 days in total so I have done the following:

  1. I've set up a for loop which loops through the 12 days.

  2. In my html I have given my element a unique id with which to target it, see below:

    <h1 id="dynamicTitle" class="CustomFont leftHeading shadow">On The <span></span> <em>of rest of generic text</em></h1>
    
  3. Then, inside my for loop I have the following code:

    $("#dynamicTitle span").html(i);
    var day = i;
    if (day == 1) {
        day = i + "st";
    } else if (day == 2) {
        day = i + "nd"
    } else if (day == 3) {
        day = i + "rd"
    }
    

UPDATE

This is the entire for loop as requested:

$(document).ready(function () {
    for (i = 1; i <= 12; i++) {
        var classy = "";
        if (daysTilDate(i + 19) > 0) {
            classy = "future";
            $("#Day" + i).addClass(classy);
            $("#mainHeading").html("");
            $("#title").html("");
            $("#description").html("");
        } else if (daysTilDate(i + 19) < 0) {
            classy = "past";
            $("#Day" + i).addClass(classy);
            $("#title").html("");
            $("#description").html("");
            $("#mainHeading").html("");
            $(".cta").css('display', 'none');
            $("#Day" + i + " .prizeLink").attr("href", "" + i + ".html");
        } else {
            classy = "current";
            $("#Day" + i).addClass(classy);
            $("#title").html(headings[i - 1]);
            $("#description").html(descriptions[i - 1]);
            $(".cta").css('display', 'block');
            $("#dynamicImage").attr("src", ".." + i + ".jpg");
            $("#mainHeading").html("");
            $(".claimPrize").attr("href", "" + i + ".html");
            $("#dynamicTitle span").html(i);
            var day = i;
            if (day == 1) {
                day = i + "st";
            } else if (day == 2) {
                day = i + "nd"
            } else if (day == 3) {
                day = i + "rd"
            } else if (day) {
            }
        }
    }

I would like to dynamically generate a string of text based on a current day. So, for example, if it is day 1 then I would like my code to generate = "Its the <dynamic>1*<dynamic string>st</dynamic string>*</dynamic>".

There are 12 days in total so I have done the following:

  1. I've set up a for loop which loops through the 12 days.

  2. In my html I have given my element a unique id with which to target it, see below:

    <h1 id="dynamicTitle" class="CustomFont leftHeading shadow">On The <span></span> <em>of rest of generic text</em></h1>
    
  3. Then, inside my for loop I have the following code:

    $("#dynamicTitle span").html(i);
    var day = i;
    if (day == 1) {
        day = i + "st";
    } else if (day == 2) {
        day = i + "nd"
    } else if (day == 3) {
        day = i + "rd"
    }
    

UPDATE

This is the entire for loop as requested:

$(document).ready(function () {
    for (i = 1; i <= 12; i++) {
        var classy = "";
        if (daysTilDate(i + 19) > 0) {
            classy = "future";
            $("#Day" + i).addClass(classy);
            $("#mainHeading").html("");
            $("#title").html("");
            $("#description").html("");
        } else if (daysTilDate(i + 19) < 0) {
            classy = "past";
            $("#Day" + i).addClass(classy);
            $("#title").html("");
            $("#description").html("");
            $("#mainHeading").html("");
            $(".cta").css('display', 'none');
            $("#Day" + i + " .prizeLink").attr("href", "" + i + ".html");
        } else {
            classy = "current";
            $("#Day" + i).addClass(classy);
            $("#title").html(headings[i - 1]);
            $("#description").html(descriptions[i - 1]);
            $(".cta").css('display', 'block');
            $("#dynamicImage").attr("src", ".." + i + ".jpg");
            $("#mainHeading").html("");
            $(".claimPrize").attr("href", "" + i + ".html");
            $("#dynamicTitle span").html(i);
            var day = i;
            if (day == 1) {
                day = i + "st";
            } else if (day == 2) {
                day = i + "nd"
            } else if (day == 3) {
                day = i + "rd"
            } else if (day) {
            }
        }
    }
Share Improve this question edited May 1, 2015 at 13:56 Salman Arshad 272k84 gold badges441 silver badges533 bronze badges asked Nov 29, 2012 at 13:53 Antonio VasilevAntonio Vasilev 2,9552 gold badges15 silver badges17 bronze badges 6
  • 1 If your source code is short enough, would you mind posting the full thing, and also saying exactly what is wrong or what's confusing you? – RonaldBarzell Commented Nov 29, 2012 at 13:56
  • 1 What is it that your code does/doesn't do currently? You did not clearly state what's going wrong. – Pointy Commented Nov 29, 2012 at 13:56
  • I'm guessing the code shown is the content of an if block which is further contained by the loop? Show more code.... – MrCode Commented Nov 29, 2012 at 13:59
  • @MrCode - Yes you are correct. I have updated the post to include the entire for loop. I hope this clears it up! – Antonio Vasilev Commented Nov 29, 2012 at 14:09
  • neat and it works well. – Dan Jay Commented Jan 3, 2014 at 3:29
 |  Show 1 more comment

25 Answers 25

Reset to default 543

For English ordinal suffix, the rules are as follows:

  • st is used with numbers ending in 1 (e.g. 1st, pronounced first)
  • nd is used with numbers ending in 2 (e.g. 92nd, pronounced ninety-second)
  • rd is used with numbers ending in 3 (e.g. 33rd, pronounced thirty-third)
  • As an exception to the above rules, numbers ending with 11, 12, and 13 use -th (e.g. 11th, pronounced eleventh, 112th, pronounced one hundred [and] twelfth)
  • th is used for all other numbers (e.g. 9th, pronounced ninth)

The following JavaScript code accomplishes this:

function ordinal_suffix_of(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";
}

// output for numbers between 0-115

for (let n = 0; n <= 115; n++) {
    console.log(n + " " + ordinal_suffix_of(n));
}

From Shopify

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

[-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))
);

Minimal one-line approach for ordinal suffixes

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

(this is for positive integers, see below for other variations)

Explanation

Start with an array with the suffixes ["st", "nd", "rd"]. We want to map integers ending in 1, 2, 3 (but not ending in 11, 12, 13) to the indexes 0, 1, 2.

Other integers (including those ending in 11, 12, 13) can be mapped to anything else—indexes not found in the array will evaluate to undefined. This is falsy in javascript and with the use of logical or (|| "th") the expression will return "th" for these integers, which is exactly what we want.

The expression ((n + 90) % 100 - 10) % 10 - 1 does the mapping. Breaking it down:

  • (n + 90) % 100: This expression takes the input integer − 10 mod 100, mapping 10 to 0, ... 99 to 89, 0 to 90, ..., 9 to 99. Now the integers ending in 11, 12, 13 are at the lower end (mapped to 1, 2, 3).
  • - 10: Now 10 is mapped to −10, 19 to −1, 99 to 79, 0 to 80, ... 9 to 89. The integers ending in 11, 12, 13 are mapped to negative integers (−9, −8, −7).
  • % 10: Now all integers ending in 1, 2, or 3 are mapped to 1, 2, 3. All other integers are mapped to something else (11, 12, 13 are still mapped to −9, −8, −7).
  • - 1: Subtracting one gives the final mapping of 1, 2, 3 to 0, 1, 2.

Verifying that it works

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

//test integers from 1 to 124
for(var r = [], i = 1; i < 125; i++) r.push(i + nth(i));

//output result
document.getElementById('result').innerHTML = r.join('<br>');
<div id="result"></div>

Variations

Allowing negative integers:

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

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

//test integers from 15 to -124
for(var r = [], i = 15; i > -125; i--) r.push(i + nth(i));

//output result
document.getElementById('result').innerHTML = r.join('<br>');
<div id="result"></div>

In ES6 fat arrow syntax (anonymous function):

n=>["st","nd","rd"][(((n<0?-n:n)+90)%100-10)%10-1]||"th"

Intl.PluralRules, the standard method.

I would just like to drop the canonical way of doing this in here, as nobody seems to know it. Do not reinvent the wheel.

If you want your code to be

  • self-documenting
  • easy to localize
  • with the modern standard

this is the way to go.

const english_ordinal_rules = new Intl.PluralRules("en", {type: "ordinal"});
const suffixes = {
    one: "st",
    two: "nd",
    few: "rd",
    other: "th"
};
function ordinal(number/*: number */) {
    const category = english_ordinal_rules.select(number);
    const suffix = suffixes[category];
    return (number + suffix);
} // -> string

const test = Array(201)
    .fill()
    .map((_, index) => index - 100)
    .map(ordinal)
    .join(" ");
console.log(test);

  • The Intl.PluralRules constructor (Draft ECMA-402)
  • Unicode’s six plurality categories

Code-golf

While I do not recommend golfing with your code and killing the readability, I came up with one for those golfers (92 bytes):

n=>n+{e:"st",o:"nd",w:"rd",h:"th"}[new Intl.PluralRules("en",{type:"ordinal"}).select(n)[2]]

You can use the moment libraries local data functions.

Code:

moment.localeData().ordinal(1)
//1st

By splitting the number into an array and reversing we can easily check the last 2 digits of the number using array[0] and array[1].

If a number is in the teens array[1] = 1 it requires "th".

function getDaySuffix(num)
{
    var array = ("" + num).split("").reverse(); // E.g. 123 = array("3","2","1")
    
    if (array[1] != "1") { // Number is not in the teens
        switch (array[0]) {
            case "1": return "st";
            case "2": return "nd";
            case "3": return "rd";
        }
    }
    
    return "th";
}

You've only got 12 days? I'd be tempted to make it just a simple lookup array:

var suffixes = ['','st','nd','rd','th','th','th','th','th','th','th','th','th'];

then

var i = 2;
var day = i + suffixes[i]; // result: '2nd'

or

var i = 8;
var day = i + suffixes[i]; // result: '8th'
function getSuffix(n) {return n < 11 || n > 13 ? ['st', 'nd', 'rd', 'th'][Math.min((n - 1) % 10, 3)] : 'th'}

I wrote this function to solve this problem:

// this is for adding the ordinal suffix, turning 1, 2 and 3 into 1st, 2nd and 3rd
Number.prototype.addSuffix=function(){
    var n=this.toString().split('.')[0];
    var lastDigits=n.substring(n.length-2);
    //add exception just for 11, 12 and 13
    if(lastDigits==='11' || lastDigits==='12' || lastDigits==='13'){
        return this+'th';
    }
    switch(n.substring(n.length-1)){
        case '1': return this+'st';
        case '2': return this+'nd';
        case '3': return this+'rd';
        default : return this+'th';
    }
};

With this you can just put .addSuffix() to any number and it will result in what you want. For example:

var number=1234;
console.log(number.addSuffix());
// console will show: 1234th

An alternative version of the ordinal function could be as follows:

function toCardinal(num) {
    var ones = num % 10;
    var tens = num % 100;

    if (tens < 11 || tens > 13) {
        switch (ones) {
            case 1:
                return num + "st";
            case 2:
                return num + "nd";
            case 3:
                return num + "rd";
        }
    }

    return num + "th";
}

The variables are named more explicitly, uses camel case convention, and might be faster.

const getOrdinalNum = (n) => n + (n > 0 ? ['th', 'st', 'nd', 'rd'][(n > 3 && n < 21) || n % 10 > 3 ? 0 : n % 10] : '');

A version of the Intl.PluralRules answer that TypeScript type checking approves of:

const englishOrdinalRules = new Intl.PluralRules('en', { type: 'ordinal' })
/**
 * Format a number as an ordinal, e.g. `3` to `3rd`.
 * @link https://stackoverflow.com/a/57518703/223225
 * @param {number} number To format
 */
export function formatNumberOrdinal(number) {
    const category = englishOrdinalRules.select(number)

    switch (category) {
        case 'one': {
            return `${number}st`
        }

        case 'two': {
            return `${number}nd`
        }

        case 'few': {
            return `${number}rd`
        }

        default: {
            return `${number}th`
        }
    }
}

I wrote this simple function the other day. Although for a date you don't need the larger numbers, this will cater for higher values too (1013th, 36021st etc...)

var fGetSuffix = function(nPos){

    var sSuffix = "";

    switch (nPos % 10){
        case 1:
            sSuffix = (nPos % 100 === 11) ? "th" : "st";
            break;
        case 2:
            sSuffix = (nPos % 100 === 12) ? "th" : "nd";
            break;
        case 3:
            sSuffix = (nPos % 100 === 13) ? "th" : "rd";
            break;
        default:
            sSuffix = "th";
            break;
    }

    return sSuffix;
};

function ordsfx(a){return["th","st","nd","rd"][(a=~~(a<0?-a:a)%100)>10&&a<14||(a%=10)>3?0:a]}

See annotated version at https://gist.github.com/furf/986113#file-annotated-js

Short, sweet, and efficient, just like utility functions should be. Works with any signed/unsigned integer/float. (Even though I can't imagine a need to ordinalize floats)

Strongly recommend the excellent date-fns library. Fast, modular, immutable, works with standard dates.

import * as DateFns from 'date-fns';

const ordinalInt = DateFns.format(someInt, 'do');

See date-fns docs: https://date-fns.org/v2.0.0-alpha.9/docs/format

Here is another option.

function getOrdinalSuffix(day) {
        
   	if(/^[2-3]?1$/.test(day)){
   		return 'st';
   	} else if(/^[2-3]?2$/.test(day)){
   		return 'nd';
   	} else if(/^[2-3]?3$/.test(day)){
   		return 'rd';
   	} else {
   		return 'th';
   	}
        
}
    
console.log(getOrdinalSuffix('1'));
console.log(getOrdinalSuffix('13'));
console.log(getOrdinalSuffix('22'));
console.log(getOrdinalSuffix('33'));

Notice the exception for the teens? Teens are so akward!

Edit: Forgot about 11th and 12th

Old one I made for my stuff...

function convertToOrdinal(number){
    if (number !=1){
        var numberastext = number.ToString();
        var endchar = numberastext.Substring(numberastext.Length - 1);
        if (number>9){
            var secondfromendchar = numberastext.Substring(numberastext.Length - 1);
            secondfromendchar = numberastext.Remove(numberastext.Length - 1);
        }
        var suffix = "th";
        var digit = int.Parse(endchar);
        switch (digit){
            case 3:
                if(secondfromendchar != "1"){
                    suffix = "rd";
                    break;
                }
            case 2:
                if(secondfromendchar != "1"){
                    suffix = "nd";
                    break;
                }
            case 1:
                if(secondfromendchar != "1"){
                    suffix = "st";
                    break;
                }
            default:
                suffix = "th";
                break;
         }
            return number+suffix+" ";
     } else {
            return;
     }
}

I wrote this function for higher numbers and all test cases

function numberToOrdinal(num) {
    if (num === 0) {
        return '0'
    };
    let i = num.toString(), j = i.slice(i.length - 2), k = i.slice(i.length - 1);
    if (j >= 10 && j <= 20) {
        return (i + 'th')
    } else if (j > 20 && j < 100) {
        if (k == 1) {
            return (i + 'st')
        } else if (k == 2) {
            return (i + 'nd')
        } else if (k == 3) {
            return (i + 'rd')
        } else {
            return (i + 'th')
        }
    } else if (j == 1) {
        return (i + 'st')
    } else if (j == 2) {
        return (i + 'nd')
    } else if (j == 3) {
        return (i + 'rd')
    } else {
        return (i + 'th')
    }
}

Here's a slightly different approach (I don't think the other answers do this). I'm not sure whether I love it or hate it, but it works!

export function addDaySuffix(day: number) { 
  const suffixes =
      '  stndrdthththththththththththththththththstndrdthththththththst';
    const startIndex = day * 2;

    return `${day}${suffixes.substring(startIndex, startIndex + 2)}`;
  }

I would like to quote the answer available in the link

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

I wanted to provide a functional answer to this question to complement the existing answer:

const ordinalSuffix = ['st', 'nd', 'rd']
const addSuffix = n => n + (ordinalSuffix[(n - 1) % 10] || 'th')
const numberToOrdinal = n => `${n}`.match(/1\d$/) ? n + 'th' : addSuffix(n)

we've created an array of the special values, the important thing to remember is arrays have a zero based index so ordinalSuffix[0] is equal to 'st'.

Our function numberToOrdinal checks if the number ends in a teen number in which case append the number with 'th' as all then numbers ordinals are 'th'. In the event that the number is not a teen we pass the number to addSuffix which adds the number to the ordinal which is determined by if the number minus 1 (because we're using a zero based index) mod 10 has a remainder of 2 or less it's taken from the array, otherwise it's 'th'.

sample output:

numberToOrdinal(1) // 1st
numberToOrdinal(2) // 2nd
numberToOrdinal(3) // 3rd
numberToOrdinal(4) // 4th
numberToOrdinal(5) // 5th
numberToOrdinal(6) // 6th
numberToOrdinal(7) // 7th
numberToOrdinal(8) // 8th
numberToOrdinal(9) // 9th
numberToOrdinal(10) // 10th
numberToOrdinal(11) // 11th
numberToOrdinal(12) // 12th
numberToOrdinal(13) // 13th
numberToOrdinal(14) // 14th
numberToOrdinal(101) // 101st

I strongly recommend this, it is super easy and straightforward to read. I hope it help?

  • It avoid the use of negative integer i.e number less than 1 and return false
  • It return 0 if input is 0
function numberToOrdinal(n) {

  let result;

  if(n < 0){
    return false;
  }else if(n === 0){
    result = "0";
  }else if(n > 0){

    let nToString = n.toString();
    let lastStringIndex = nToString.length-1;
    let lastStringElement = nToString[lastStringIndex];

    if( lastStringElement == "1" && n % 100 !== 11 ){
      result = nToString + "st";
    }else if( lastStringElement == "2" && n % 100 !== 12 ){
      result = nToString + "nd";
    }else if( lastStringElement == "3" && n % 100 !== 13 ){
      result = nToString + "rd";
    }else{
      result = nToString + "th";
    }

  }

  return result;
}

console.log(numberToOrdinal(-111));
console.log(numberToOrdinal(0));
console.log(numberToOrdinal(11));
console.log(numberToOrdinal(15));
console.log(numberToOrdinal(21));
console.log(numberToOrdinal(32));
console.log(numberToOrdinal(43));
console.log(numberToOrdinal(70));
console.log(numberToOrdinal(111));
console.log(numberToOrdinal(300));
console.log(numberToOrdinal(101));

OUTPUT

false
0
11th
15th
21st
32nd
43rd
70th
111th
300th
101st

It can be done as follow:

function convertNumberToOrdinal(inputNumber) {
    if (typeof inputNumber !== 'number') {
        return 'Input is not a valid';
    }

    if (inputNumber % 1 !== 0) {
        return 'Input is not an integer';
    }

    if (inputNumber < 0) {
        return 'Input must be a non-negative integer';
    }

    const lastDigit = inputNumber % 10;
    const lastTwoDigits = inputNumber % 100;

    if (lastTwoDigits >= 11 && lastTwoDigits <= 13) {
        return inputNumber + 'th';
    }

    switch (lastDigit) {
        case 1:
            return inputNumber + 'st';
        case 2:
            return inputNumber + 'nd';
        case 3:
            return inputNumber + 'rd';
        default:
            return inputNumber + 'th';
    }
}

// Example usage:
console.log(convertNumberToOrdinal(1));   // Output: "1st"
console.log(convertNumberToOrdinal(22));  // Output: "22nd"
console.log(convertNumberToOrdinal(33));  // Output: "33rd"
console.log(convertNumberToOrdinal(104)); // Output: "104th"

This is for one liners and lovers of es6

let i= new Date().getDate 

// I can be any number, for future sake we'll use 9

const j = I % 10;
const k = I % 100;

i = `${i}${j === 1 &&  k !== 11 ? 'st' : j === 2 && k !== 12 ? 'nd' : j === 3 && k !== 13 ? 'rd' : 'th'}`}

console.log(i) //9th

Another option for +be number would be:

console.log(["st","nd","rd"][((i+90)%100-10)%10-1]||"th"]

Also to get rid of the ordinal prefix just use these:

console.log(i.parseInt("8th"))

console.log(i.parseFloat("8th"))

feel free to modify to suit you need

<p>31<sup>st</sup> March 2015</p>

You can use

1<sup>st</sup> 2<sup>nd</sup> 3<sup>rd</sup> 4<sup>th</sup>

for positioning the suffix

发布评论

评论列表(0)

  1. 暂无评论