12

I am trying to display numbers within a particular table with ordinal suffixes. The table always shows three numbers which come from an XML file. The numbers show ranks, so for example they may be 6th, 120th, 131st. The output is a table that would look like this:

<table>
    <tr>
        <td class='ordinal'>6</td>
        <td class='ordinal'>120</td>
        <td class='ordinal'>131</td>
    </tr>
</table>

I would ideally like to use javascript and I found a few very good solutions on stackoverflow, for example this one. However, I am struggling to apply the function to all numbers within the table, rather than putting in each number individually. I tried using a CSS class so that my function looks like this:

<script type="text/javascript">
$(function(){
    $(".ordinal").each(function(){
        var j = i % 10;
        if (j == 1 && i != 11) {
            return i + "st";
        }
        if (j == 2 && i != 12) {
            return i + "nd";
        }
        if (j == 3 && i != 13) {
            return i + "rd";
        }
        return i + "th";
        });
})
</script>

but it's not working, probably because I screwed up the code somewhere. Maybe somebody here can help me out and tell me where I went wrong?

Thank you very much for your help!

Community
  • 1
  • 1
rf2012
  • 233
  • 2
  • 7
  • 13

8 Answers8

13

My own suggestion, would be:

$(".ordinal").text(function (i, t) {
    i++;
    var str = i.toString().slice(-1),
        ord = '';
    switch (str) {
        case '1':
            ord = 'st';
            break;
        case '2':
            ord = 'nd';
            break;
        case '3':
            ord = 'rd';
            break;
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
        case '0':
            ord = 'th';
            break;
    }
    return i + ord;
});

JS Fiddle demo.

This effectively takes the incremented number (i++, in order to start from 1 not 0), converts it to a string, then looks at the last number of that string. This should work for any number, since the ordinal is based purely on that last number.

You could also extend the Number prototype to implement this functionality:

Number.prototype.ordinate = function(){
    var num = this + 1,
        last = num.toString().slice(-1),
        ord = '';
    switch (last) {
        case '1':
            ord = 'st';
            break;
        case '2':
            ord = 'nd';
            break;
        case '3':
            ord = 'rd';
            break;
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
        case '0':
            ord = 'th';
            break;
    }
    return num.toString() + ord;
};

$(".ordinal").text(function (i, t) {
    return i.ordinate();
});

JS Fiddle demo.

Edited to offer a slight alternative:

Number.prototype.ordinate = function(){
    var num = this,
        last = num.toString().slice(-1),
        ord = '';
    switch (last) {
        case '1':
            ord = 'st';
            break;
        case '2':
            ord = 'nd';
            break;
        case '3':
            ord = 'rd';
            break;
        default:
            ord = 'th';
            break;
    }
    return num.toString() + ord;
};

$(".ordinal").text(function (i,t) {
    return t.replace(/(\d+)/g, function(a){
        return parseInt(a, 10).ordinate();
    });
});

JS Fiddle demo.

This essentially iterates over each .ordinal element, replacing the numbers present with the (same) numbers with the ordinal suffix added to it.


Edited to address the problem, raised in the comments, below, that 11, 12 and 13 were receiving the ordinal suffix of st, nd and rd (respectively). This is now corrected to being th in all cases:

Number.prototype.ordinate = function(){
    var num = this,
        numStr = num.toString(),
        last = numStr.slice(-1),
        len = numStr.length,
        ord = '';
    switch (last) {
        case '1':
            ord = numStr.slice(-2) === '11' ? 'th' : 'st';
            break;
        case '2':
            ord = numStr.slice(-2) === '12' ? 'th' : 'nd';
            break;
        case '3':
            ord = numStr.slice(-2) === '13' ? 'th' : 'rd';
            break;
        default:
            ord = 'th';
            break;
    }
    return num.toString() + ord;
};

JS Fiddle demo.

References:

David Thomas
  • 249,100
  • 51
  • 377
  • 410
  • This looks interesting, thank you David, but it doesn't seems to attach 1st, 2nd, 3rd, etc. to the 1st row, 2nd row, 3rd row, etc. I would like the "st", "nd", "rd" attached to the number in table cell. I couldn't get that to work, is there a way to do that? – rf2012 Apr 04 '13 at 12:58
  • Yes, that's exactly what happens for me, under Chrome 25/Win XP. What browser/platform are you using that fails? (I'm unable to test with IE, since IE<8 fails JS Fiddle as a whole.) – David Thomas Apr 04 '13 at 15:22
  • Oh boy, I reread my comment, and it is pretty confusing, I apologize. The sentence was supposed to mean "it seems to attach...". The point, however, is that I don't want the cells of the table numbered in sequence but simply the number within the cell shown as an ordinal. Does that make more sense? I apologize for my previous unclear comment. I tested it on Chrome 26/Win 7. – rf2012 Apr 04 '13 at 15:44
  • Awesome, that works great!!! Now there's only a last tiny problem. Is there a way to display 11-13 (and 111-113, and so forth) as 11th, 12th, and 13th rather than 11st, 12nd, 13rd as it does right now. I tired adding `case '13'` but that didn't do the trick. Thank you so much for all your help! – rf2012 Apr 04 '13 at 18:32
  • Okay, I *think* this is fixed, now? – David Thomas Apr 04 '13 at 21:40
  • YES, I _think_ so too :-) Thank you so much, David!!! I really appreciate your help and patience! – rf2012 Apr 04 '13 at 22:32
  • You're very welcome, I'm glad to have been of help! And I'm somewhat dismayed it didn't occur to me to treat numbers ending in `11`, `12` and `13` differently when I wrote the answer. – David Thomas Apr 04 '13 at 22:36
  • in your last example you are not using the variable `len` anywhere – vsync Nov 27 '14 at 18:06
4
function nth(n){
    if(isNaN(n) || n%1) return n;   
    var s= n%100;
    if(s>3 && s<21) return n+'th';
    switch(s%10){
        case 1: return n+'st';
        case 2: return n+'nd';
        case 3: return n+'rd';
        default: return n+'th';
    }
}

You can take care of the teens in their own line, other integers follow the last digit rules.

kennebec
  • 102,654
  • 32
  • 106
  • 127
2

It's not working because you're returning the strings to $.each, not actually using them. Usage would depend on your HTML but here is an example of setting the .ordinal text to the value.

You were also missing the i parameter on the event handler and you can increment i to start from 1st instead of 0th.

jsFiddle

$(".ordinal").each(function (i) {
    i++;
    var j = i % 10,
        str;
    if (j == 1 && i != 11) {
        str = i + "st";
    } else if (j == 2 && i != 12) {
        str = i + "nd";
    } else if (j == 3 && i != 13) {
        str = i + "rd";
    } else {
        str = i + "th";
    }
    $(this).text(str);
});

If you have the numbers in your elements already then it would be best to not rely on the index and instead check the number, then append the string to the end.

jsFiddle

$(document).ready(function () {
    $(".ordinal").each(function (i) {
        var j = parseInt($('ordinal').text(), 10) % 10,
            str;
        if (j == 1 && i != 11) {
            str = "st";
        } else if (j == 2 && i != 12) {
            str = "nd";
        } else if (j == 3 && i != 13) {
            str = "rd";
        } else {
            str = "th";
        }
        var elem = $(this)
        elem.text(elem.text() + str);
    });
});
Daniel Imms
  • 47,944
  • 19
  • 150
  • 166
  • Awesome, thank you, that worked! However, now I have the problem that every number, for example 132, is returned as 17th, not 132nd. My numbers go up to over 200. Do you know where that mistake may be rooted? – rf2012 Apr 04 '13 at 11:40
  • Works fine for me http://jsfiddle.net/Tyriar/gWnUJ/ did you miss something in the code? – Daniel Imms Apr 04 '13 at 11:43
  • Ah, I asked the question before seeing the i++, I'm sorry. However, no it returns the numbers as simply 1st, 2nd, 3rd, no as, say, 17th, 132nd, 146th. I'm feeding the numbers in through an XML document, in case that makes a difference!? So the code would look like this: `
    4
    ` and should return 4th...
    – rf2012 Apr 04 '13 at 11:44
  • Updated with a more suitable solution for elements containing the number. – Daniel Imms Apr 04 '13 at 11:52
  • Awesome, thank you!!! I was just in the process of updating my original question. Now, the only problem is that 1 returns as 1th. Is it possible to retain the 'st', 'nd', and 'rd' endings for all numbers ending with 1,2, or 3, except for 11,12,13? – rf2012 Apr 04 '13 at 11:57
2

I created two approaches one using Prototype, the other as a plugin :

Number.prototype.between = function(n,m){ return this > n && this < m }
Number.prototype.ORDINATE_INDEX = ["th","st","nd","rd"];
Number.prototype.toOrdinate = function(){
    var
        nthMod = (this % 10),
        index =  nthMod > 3 || this.between(10,20) ? 0 : nthMod
    ;

    return this + this.ORDINATE_INDEX[index];
};

$(".ordinal").text(function (index, element) {
    return parseInt(element).toOrdinate();
});

This is the one as a Jquery Plugin :

(function($){
    var numberTool = new (function(){
        var private = {};

        private.ORDINATE_INDEX = ["th","st","nd","rd"];

        private.parseOrdinary = function(number)
        {
            var
                nthMod = (number % 10),
                index =  nthMod > 3 || private.between(number, 10,20) ? 0 : nthMod
            ;

            return number + private.ORDINATE_INDEX[index];
        }

        private.between = function(number, n,m){
            return number > n && number < m
        }

        this.isNumeric = function(number)
        {
            return !isNaN(parseFloat(number)) && isFinite(number);
        }

        this.toOrdinary = function(number)
        {
            return this.isNumeric(number) ? private.parseOrdinary(number) : number;
        }
    });


    $.fn.toOrdinary = function(){
        return this.each(function(){
            $element = $(this);
            $element.text(numberTool.toOrdinary($element.text()));
        }); 
    };
})(jQuery);

$(".ordinal").toOrdinary();
$(".ordinal").toOrdinary();
$(".ordinal").toOrdinary();

Tested on JSFIDDLE:

The prototype version example : http://jsfiddle.net/f8vQr/6/

The JQuery version example : http://jsfiddle.net/wCdKX/27/

lac_dev
  • 1,336
  • 14
  • 20
1

Ordinal suffix in one line

var integerWithSuffix=originalInteger+(['st','nd','rd'][( originalInteger +'').match(/1?\d\b/)-1]||'th');

the concatenation of the original number and a string representing the ordinal derived from an array indexed by the result of a regex search on that number

http://jsfiddle.net/thisishardcoded/DbSMB/

Jim
  • 179
  • 3
  • 12
  • Can you explain the RegExp a bit more, or link to the source where you found this?! – yckart Aug 15 '13 at 21:31
  • The regex will select either a single digit, if a single digit is given, or the last digit in a set of digits, 1 will look at 1, and 123 will look at 3, to determine the suffix. Maybe the beginning 1? isn't needed, as \d\b will catch a single digit or the last digit in a set of digits. – Danny Nov 17 '14 at 20:11
0

I would do something like this, based on David Thomas's answer:

Number.prototype.ordinate = function(){
    var num = this,
        ones = num % 10, //gets the last digit
        tens = num % 100, //gets the last two digits
        ord = ["st","nd","rd"][ tens > 10 && tens < 20 ? null : ones-1 ] || 'th';
    return num.toString() + ord;
};

It accomplishes the same thing. If a number's last 2 digits are within the 11-19 range OR the last digit is between 4-0 it defaults to 'th', otherwise it will pull a 'st', 'nd' or 'rd' out of the array based on the ones place.

I like the idea of creating a prototype function very much but I would definitely leave the incrementation of the index outside of the prototype function to make it more versatile:

$(".ordinal").text(function (i, t) {
    return (++i).ordinate();
});

JS Fiddle Demo

CSharp
  • 629
  • 6
  • 6
0

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)

hndcrftd
  • 3,180
  • 1
  • 21
  • 18
0

This is what I use, and it works for any year, month, day (leap year) included:

// panelyear, panelmonth and panelday are passed as parameters

var PY01 = parseInt(panelyear); var PM01 = (parseInt(panelmonth) - 1); PD01 = parseInt(panelday);

var now = new Date(PY01, PM01, PD01);
var start = new Date(PY01, 0, 0);
var diff = (now - start) + ((start.getTimezoneOffset() - now.getTimezoneOffset()) * 60 * 1000);
var oneDay = 1000 * 60 * 60 * 24;
var day = Math.floor(diff / oneDay);

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

Use with

<script> document.write(getNumberWithOrdinal(day)); </script>
ventaquil
  • 2,780
  • 3
  • 23
  • 48
John
  • 9
  • 2