给数字添加序数后缀(st,nd,rd和th)

261

我希望能动态生成一段文本字符串,基于当前的日期。例如,如果是第1天,我希望我的代码生成 = "Its the <dynamic>1*<dynamic string>st</dynamic string>*</dynamic>" 。

总共有12天,所以我已经完成了以下操作:

  1. 我设置了一个循环,循环遍历这12天。

  2. 在我的html中,我为元素分配了一个唯一的id,以便对其进行定位,如下所示:

    <h1 id="dynamicTitle" class="CustomFont leftHeading shadow">On The <span></span> <em>of rest of generic text</em></h1>
    
  3. 接下来,在我的 for 循环内部,我有以下代码:

  4. $("#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"
    }
    

更新

这是所请求的完整循环:

$(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) {
            }
        }
    }

1
如果你的源代码足够简短,你介意贴出完整的代码,并且说明具体有什么问题或者什么让你感到困惑吗? - RonaldBarzell
1
你的代码目前是做什么/不做什么?你没有清楚地说明出了什么问题。 - Pointy
我猜测所展示的代码是一个if块的内容,而该块进一步被循环包含?请展示更多代码... - MrCode
@MrCode - 是的,你说得对。我已经更新了帖子,包括整个for循环。希望这样能澄清问题! - Antonio Vasilev
整洁并且运行良好。 - Dan Jay
截至2021年,更本地化和友好的答案是: https://dev59.com/UmYr5IYBdhLWcg3wfaWd#57518703 - Jay Wick
25个回答

528

规则如下:

  • 1结尾的数字使用st(例如1st,读作first)
  • 2结尾的数字使用nd(例如92nd,读作ninety-second)
  • 3结尾的数字使用rd(例如33rd,读作thirty-third)
  • 作为上述规则的例外,所有以11、12或13结尾的“teen”数字都使用-th(例如11th,读作eleventh,112th, 读作one hundred [and] twelfth)
  • 所有其他数字都使用th(例如9th,读作ninth)。

以下JavaScript代码(在Jun '14中重写)可以实现此功能:

function ordinal_suffix_of(i) {
    var 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";
}

0-115之间的数字样本输出:

  0  0th
  1  1st
  2  2nd
  3  3rd
  4  4th
  5  5th
  6  6th
  7  7th
  8  8th
  9  9th
 10  10th
 11  11th
 12  12th
 13  13th
 14  14th
 15  15th
 16  16th
 17  17th
 18  18th
 19  19th
 20  20th
 21  21st
 22  22nd
 23  23rd
 24  24th
 25  25th
 26  26th
 27  27th
 28  28th
 29  29th
 30  30th
 31  31st
 32  32nd
 33  33rd
 34  34th
 35  35th
 36  36th
 37  37th
 38  38th
 39  39th
 40  40th
 41  41st
 42  42nd
 43  43rd
 44  44th
 45  45th
 46  46th
 47  47th
 48  48th
 49  49th
 50  50th
 51  51st
 52  52nd
 53  53rd
 54  54th
 55  55th
 56  56th
 57  57th
 58  58th
 59  59th
 60  60th
 61  61st
 62  62nd
 63  63rd
 64  64th
 65  65th
 66  66th
 67  67th
 68  68th
 69  69th
 70  70th
 71  71st
 72  72nd
 73  73rd
 74  74th
 75  75th
 76  76th
 77  77th
 78  78th
 79  79th
 80  80th
 81  81st
 82  82nd
 83  83rd
 84  84th
 85  85th
 86  86th
 87  87th
 88  88th
 89  89th
 90  90th
 91  91st
 92  92nd
 93  93rd
 94  94th
 95  95th
 96  96th
 97  97th
 98  98th
 99  99th
100  100th
101  101st
102  102nd
103  103rd
104  104th
105  105th
106  106th
107  107th
108  108th
109  109th
110  110th
111  111th
112  112th
113  113th
114  114th
115  115th

13
当前的代码没有正确处理三个以“teen”结尾的数字的例外情况。比如,数字 111112113 应该分别输出为 "111th""112th""113th",而不是当前函数所产生的 "111st""112nd""113rd"。请修正此问题。 - martineau
3
这个回答似乎发展得很完美。感谢所有贡献者。 - Lonnie Best
14
我将其转换为ES6语法的lambda表达式,但实际上没有什么理由:n => n + (n % 10 == 1 && n % 100 != 11 ? 'st' : n % 10 == 2 && n % 100 != 12 ? 'nd' : n % 10 == 3 && n % 100 != 13 ? 'rd' : 'th') - Camilo Martin
5
有趣的是,π/2年后我仍然能够阅读它。 - Camilo Martin
1
@Megandardery 稍微简短一些,稍微愚蠢一些:n=>n+['th','st','nd','rd'][n%100-n%10-10&&n%10<4?n%10:0] - Potassium Ion
显示剩余9条评论

252

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


36
添加解释可以使答案更好! - Pugazh
5
如果v的个位数大于等于20(第20到99个数),则查找s[v%10]。如果未找到,则尝试查找s[v](第0到3个数)。如果仍然未找到,则使用s[0](第4到19个数)。 - Sheepy
4
为什么函数名中要有双倍的“get”呢? - Gisheri

109

一种单行最简方式添加序号后缀

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

(这仅适用于正整数,其他变量如下所示)

解释

从后缀数组["st", "nd", "rd"]开始。我们希望将以1、2、3结尾的整数(但不是以11、12、13结尾的)映射到索引0、1、2。

其他整数(包括以11、12、13结尾的)可以映射到任何其他值-在数组中找不到索引的会得到undefined。在JavaScript中,这是不真实的,借助逻辑或(|| "th")表达式将返回"th",这正是我们想要的结果。

  

表达式((n+90)%100-10)%10-1进行映射。拆分它:

  • (n+90)%100: 该表达式将输入整数- 10 mod 100,将10映射为0,... 99映射为89,0映射为90,... 9 映射为99。现在,以11、12、13结尾的整数位于较低端(映射为1、2、3)。
  • -10:现在,将10映射到-10、19映射到-1、99映射到79,0映射到80,...9映射到89。以11、12、13结尾的整数都映射为负整数(-9、-8、-7)。
  • %10:现在,所有以1、2或3结尾的整数都被映射为1、2、3。所有其他整数都映射为其他值(11、12、13仍映射为-9、-8、-7)。
  • -1:减1可以将1、2、3的最终映射结果变为0、1、2。

验证其是否有效

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>

变化

允许负整数:

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>

在ES6的箭头函数语法中(匿名函数):

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

8
优雅。我最喜欢的。 - guy mograbi

88

Intl.PluralRules,标准的方法。

我想在这里介绍一种经典的方法,因为似乎没有人知道它。不要重复发明轮子。

如果您希望您的代码具备以下特点:

  • 自描述
  • 易于本地化
  • 符合现代标准

这就是正确方式。

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


Code-golf

虽然我不建议在代码上使用高尔夫,并且降低可读性,但我为那些高尔夫球手想出了一个(92 字节):

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

@RobG 托马斯·朗卡斯的回答中,他所说允许负数的示例实际上并不允许负数? - BobRodes
4
我仍然最喜欢这一个,部分原因是你可以进行本地化。 - BobRodes
@RobG,@BobRhodes,你们试过这个处理负数的变体吗?function nth(n){return["st","nd","rd"][(((n<0?-n:n)+90)%100-10)%10-1]||"th"}。我觉得它很好用,我还添加了演示代码。 - Tomas Langkaas
2
谢谢分享这个解决方案!我喜欢能够利用现有的功能。 - BenjiFB
1
如果需要与类型检查(例如TypeScript)兼容的版本,请参见https://dev59.com/UmYr5IYBdhLWcg3wfaWd#76265684。 - Geoffrey Booth

29

1
如果您的代码库中已经使用了moment,那么最简洁的方法就是这样做! - NeERAJ TK
如果您的代码库中已经在使用moment,那么最干净的方法就是这样做! - NeERAJ TK

10

通过将数字划分为数组并翻转,我们可以使用 array[0]array[1] 轻松检查数字的最后2位。

如果一个数字是在十几岁,array[1] = 1 则需要加上“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";
}

你必须排除11、12、13。 - psx
2
@psx 这就是第5行的条件所用之处。 - nick
1
"Number is in the teens"应该被翻译为 "数字不在十几岁"。 :-) - RobG

7
你只有12天?我建议你可以考虑使用一个简单的查找数组:
var suffixes = ['','st','nd','rd','th','th','th','th','th','th','th','th','th'];

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

或者

var i = 8;
var day = i + suffixes[i]; // result: '8th'

谢谢,这解决了我的问题。我无法让后缀与日期匹配,所以我只是用数字和后缀填充了数组,这完美地解决了问题。然后我只需像这样调用它 $("#dynamicTitle span").html(suffix[i-1]); - Antonio Vasilev
由于从第四个索引开始的所有内容都是相同的,因此您可以执行 suffixes = ['th','st','nd','rd']; d = i + (suffixes[i] || suffixes[0]);. :-) - RobG

5
function getSuffix(n) {return n < 11 || n > 13 ? ['st', 'nd', 'rd', 'th'][Math.min((n - 1) % 10, 3)] : 'th'}

不错的一行代码,只是有点难以理解。 - bryc
1
当 n == 0 时会失败,因为 n - 1 部分会返回未定义(undefined)。对于大于110的数字也会失败,例如 getSuffix(111) 返回 "st"。 它确实解决了原始问题,其中数字介于1到12之间,但不是一般解决方案。 :-( - RobG

3
我写了这个函数来解决这个问题:
// 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';
    }
};

使用这个功能,您只需在任何数字后面添加.addSuffix()即可得到您想要的结果。例如:

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

我认为在这个字符串 var lastDigits=n.substring(number.length-2); 中的 number 应该改为 this - Slava Abakumov
应该是n而不是this,但感谢指出这个错误! :) - Jimmery

3

序数函数的另一种版本可以如下所示:

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";
}

变量名更加明确,采用驼峰命名法,并且可能会更快。


然而,这对于本地化的代码库将不起作用。 - Daniel Harvey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接