PDA

View Full Version : Ordinal Numbers



glosrfc
February 7th, 2008, 12:44 PM
There are probably many different ways of doing this, using an assortment of if, case, and switch statements. But here's the method I use...hope it proves useful to someone:

function getOrdinalNumber(num) {
return num==0 ? num : num + ['th', 'st', 'nd', 'rd'][!(num % 10 > 3 || Math.floor(num % 100 / 10) == 1) * num % 10];
}


Here's a few samples:

trace(getOrdinalNumber(11));
// returns 11th

trace("Today is Thursday " + getOrdinalNumber(7) + " February");
// returns Today is Thursday 7th February

trace("If the " + getOrdinalNumber(99) + " runner in the marathon is overtaken twice, he slips back to " + getOrdinalNumber(99 + 2) + " place");
// returns If the 99th runner in the marathon is overtaken
// twice, he slips back to 101st place

for(i=0; i<115; i++){
trace(getOrdinalNumber(i));
}


If you're planning to use it to append an ordinal suffix to dates only, you can shorten the function slightly:

function getOrdinalNumber(num) {
return num + ['th', 'st', 'nd', 'rd'][!(num % 10 > 3 || Math.floor(num % 100 / 10) == 1) * num % 10];
}



And another sample using just dates:

for(i=1; i<32; i++){
trace(getOrdinalNumber(i));
}

chacelp
February 7th, 2008, 01:20 PM
Can you explain what the ? and : operators are doing in your function. I will use this, but can't find any info on the logic behind it.

I know this might be a dumb question, but I am asking anyway.

glosrfc
February 7th, 2008, 01:28 PM
No question is ever dumb :)

It's a conditional operator...if the first expression evaluates as true, then perform the second operation, otherwise perform the third operation, e.g.

if x < 10 ? "x is one digit long" : "x is more than one digit"

Search in the Flash help files on "conditional operator" and it's explained more fully there.

In the first example, it's used to check if num = 0. If so, it simply returns num, i.e. 0. If num != 0, it performs the calculation to return the correct ordinal suffix.

With dates, there is no 0 (and no likelihood of generating a 0th, 0st, 0nd, 0rd) so the conditional operation can be safely removed.

chacelp
February 7th, 2008, 02:32 PM
Great! I knew it was some sort of conditional operand but I didn't know for sure.

So in your example you could even get rid of the 'if' like so:
function testInt(x) {
return 0 < x < 10 ? "x is a single integer" : "x is NOT a single integer";
}

glosrfc
February 7th, 2008, 02:55 PM
Yes, although the example I gave was supposed to be a simple explanation, hence the inclusion of the if word. Your testInt needs a slight alteration before it will work. As it stands, it will always return true as x is greater than 0:


function testInt(x) {
return x < 10 ? "x is a single integer" : "x is NOT a single integer";
};
trace (testInt(5));
trace (testInt(15));

And it won't work at all for any decimals, e.g.
testInt(1.5) will also return x is a single integer.

chacelp
February 7th, 2008, 03:38 PM
I have learned my one thing for today... I am going to shut down my brain now.. Thanks for the explanation!

chacelp
February 7th, 2008, 03:57 PM
Actually if anyone is so inclined can you explain the second part of the function?


['th', 'st', 'nd', 'rd'][!(num % 10 > 3 || Math.floor(num % 100 / 10) == 1) * num % 10]

McGuffin
February 7th, 2008, 04:18 PM
['th', 'st', 'nd', 'rd'][!(num % 10 > 3 || Math.floor(num % 100 / 10) == 1) * num % 10];


Translated to human

num % (modulo) 10 > 3 returns true if the remainder of the number divided by 10 is greater than 3.

Math.floor(num % 100 / 10) == 1 returns true if the remainder of the number divided by 100, then dividing the remainder by 10, floored, equals 1. Basically this looks for the numbers 1, 11, 21... continually

If one of the above returns 1, then the answer is flipped to 0 by the !. Vice versa as well, so if they return 0 it'll be 1.

The part following it simply multiplies by the remainder of the number divided by 10. So if you have entered 123, 123 % 10 = 3 and then first part has returned 1, then you're accessing the 3rd position in the array (since arrays in ActionScript are 0-based, they go from 0,1,2,3, you're really getting the 4th position). If the first part returned 0 (ie the last digit of the number is greater than 3) than you get the 0th position of the array ("th").

I believe this is right... that's beautiful code, glosrfc.

glosrfc
February 7th, 2008, 04:31 PM
Bah...he beat me to it. And that's a beautiful explanation McGuffin, thank you!

Although I might've avoided using the term "0th"...particularly as the code includes a conditional statement to prevent that term appearing ;)

Is there such a thing as a noughtth? zeroth?

Krilnon
February 7th, 2008, 04:40 PM
Is there such a thing as a noughtth? zeroth?Zeroth.

McGuffin
February 7th, 2008, 04:48 PM
Bah...he beat me to it. And that's a beautiful explanation McGuffin, thank you!

Although I might've avoided using the term "0th"...particularly as the code includes a conditional statement to prevent that term appearing ;)

Is there such a thing as a noughtth? zeroth?

Normally I'd just say the nought position, but it seemed more fitting with the ordinal numbers thing going on ;) And it seems like I wasn't completely out of left field according to Krilnon's link

chacelp
February 7th, 2008, 04:56 PM
Great, that helps me a lot. This is what I wasn't understanding:


function getOrdinalNumber(num) {
new_array = new Array("th","st","nd","rd");
return num==0 ? num : num + new_array[!(num % 10 > 3 || Math.floor(num % 100 / 10) == 1) * num % 10];
}

The array had me confused I thought there was more logic the way you had it setup, the above way is how my small, malnurished brain would have done it...

Thanks for the input everyone!

glosrfc
February 7th, 2008, 05:01 PM
It would appear that noughth is okay too
http://en.wikipedia.org/wiki/Names_of_numbers_in_English#Ordinal_numbers

But, then again, this isn't intended to deal with counts starting at zero. If you like the idea of zeroth, just use the second example I gave :D

glosrfc
February 7th, 2008, 05:10 PM
Great, that helps me a lot.
The array had me confused I thought there was more logic the way you had it setup, the above way is how my small, malnurished brain would have done it..

No problem. As you can see, it just boils down to choosing the correct item from a small array...but it uses rather convoluted logic to get there.

An alternative version which is probably a lot easier to understand would be:

function getOrdinalNumber(num) {
daySuffix = 'th';
if (num != 11 && num != 12 && num != 13) {
num = num % 10;
if (num == 1) {
daySuffix = 'st';
}
if (num == 2) {
daySuffix = 'nd';
}
if (num == 3) {
daySuffix = 'rd';
}
}
return day + daySuffix;
}
trace(getOrdinalNumber(day));

glosrfc
February 7th, 2008, 05:27 PM
Here's a practical use of the ordinal function...a calculator to predict on which day Easter occurs for any given year:



// Declare array of month names
Months = new Array("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December");

// function to calculate the date of Easter Sunday
function calcEaster(year) {
month = Math.floor((((19 * (year % 19) + Math.floor(year / 100) - Math.floor(Math.floor(year / 100) / 4) - Math.floor((Math.floor(year / 100) - Math.floor((Math.floor(year / 100) + 8) / 25) + 1) / 3) + 15) % 30) + (32 + 2 * (Math.floor(year / 100) % 4) + 2 * Math.floor((year % 100) / 4) - ((19 * (year % 19) + Math.floor(year / 100) - Math.floor(Math.floor(year / 100) / 4) - Math.floor((Math.floor(year / 100) - Math.floor((Math.floor(year / 100) + 8) / 25) + 1) / 3) + 15) % 30) - (year % 100) % 4) % 7 - 7 * Math.floor(((year % 19) + 11 * ((19 * (year % 19) + Math.floor(year / 100) - Math.floor(Math.floor(year / 100) / 4) - Math.floor((Math.floor(year / 100) - Math.floor((Math.floor(year / 100) + 8) / 25) + 1) / 3) + 15) % 30) + 22 * (32 + 2 * (Math.floor(year / 100) % 4) + 2 * Math.floor((year % 100) / 4) - ((19 * (year % 19) + Math.floor(year / 100) - Math.floor(Math.floor(year / 100) / 4) - Math.floor((Math.floor(year / 100) - Math.floor((Math.floor(year / 100) + 8) / 25) + 1) / 3) + 15) % 30) - (year % 100) % 4) % 7) / 451) + 114) / 31) - 1;
month = Months[month];
day = ((((19 * (year % 19) + Math.floor(year / 100) - Math.floor(Math.floor(year / 100) / 4) - Math.floor((Math.floor(year / 100) - Math.floor((Math.floor(year / 100) + 8) / 25) + 1) / 3) + 15) % 30) + (32 + 2 * (Math.floor(year / 100) % 4) + 2 * Math.floor((year % 100) / 4) - ((19 * (year % 19) + Math.floor(year / 100) - Math.floor(Math.floor(year / 100) / 4) - Math.floor((Math.floor(year / 100) - Math.floor((Math.floor(year / 100) + 8) / 25) + 1) / 3) + 15) % 30) - (year % 100) % 4) % 7 - 7 * Math.floor(((year % 19) + 11 * ((19 * (year % 19) + Math.floor(year / 100) - Math.floor(Math.floor(year / 100) / 4) - Math.floor((Math.floor(year / 100) - Math.floor((Math.floor(year / 100) + 8) / 25) + 1) / 3) + 15) % 30) + 22 * (32 + 2 * (Math.floor(year / 100) % 4) + 2 * Math.floor((year % 100) / 4) - ((19 * (year % 19) + Math.floor(year / 100) - Math.floor(Math.floor(year / 100) / 4) - Math.floor((Math.floor(year / 100) - Math.floor((Math.floor(year / 100) + 8) / 25) + 1) / 3) + 15) % 30) - (year % 100) % 4) % 7) / 451) + 114) % 31) + 1;
}

// function to calculate the ordinal suffix
function getNumberOrdinal(day) {
var nModTen = day % 10;
return (day + ['th', 'st', 'nd', 'rd'][nModTen > 3 ? 0 : (day % 100 - nModTen != 10) * nModTen]);
}


Sample usage:


// Example loop to calculate Easter Day from 1900 - 2050
for (i = 1900; i <= 2050; i++) {
calcEaster(i);
trace("Easter falls on Sunday " + getNumberOrdinal(day) + " of " + month + ", " + i);
}

chacelp
February 7th, 2008, 05:46 PM
holy balls batman... :thumb2:

mprzybylski
February 7th, 2008, 10:48 PM
glosrfc, tell me you didn't come up with that calcEaster function by yourself?

If you did, that's one HELL of a function.

McGuffin
February 7th, 2008, 11:03 PM
Please don't make me explain the logic behind that, chacelp :ne:

chacelp
February 8th, 2008, 12:39 PM
McGuffin - I wasn't gonna go there, trust me... I have been playing just changing numbers.. I deal with some pretty crappy date functions too at my work. For some reason they decided to record times in seconds since December 31st of 1973 at midnight so to just pull the date in SQL is pretty hairy...

:thumb:

EDIT: I am not saying your function is crappy per se I am just saying all that logic to get the day easter falls on takes a lot of time to create for something relatively simple which is crappy!

glosrfc
February 8th, 2008, 07:34 PM
glosrfc, tell me you didn't come up with that calcEaster function by yourself?

If you did, that's one HELL of a function.
Yes and no. The credit is really due to Spencer Jones (1890-1960) who, in his book General Astronomy proposed a method of calculating Easter without the need to refer to loads of mathematical and astronomical tables. All I've done is to pull it together into one, errrr...concise...AS function.


McGuffin - I wasn't gonna go there, trust me... I have been playing just changing numbers.. I deal with some pretty crappy date functions too at my work. For some reason they decided to record times in seconds since December 31st of 1973 at midnight so to just pull the date in SQL is pretty hairy...

:thumb:

EDIT: I am not saying your function is crappy per se I am just saying all that logic to get the day easter falls on takes a lot of time to create for something relatively simple which is crappy!

Don't blame me if it's crappy either...blame the Bishops at the Council of Nicaea in 325 AD, and the Synod of Whitby in 664 (plus assorted philosophers, kings, scientists, and monks, since) who devised the rules by which Easter is determined.

If you must know, the basic premise is that Easter must fall on the first Sunday following the fourteenth day after the first full moon to occur after the vernal equinox*. However, the Bishops et al decreed that lunar months were exactly 29 days long which meant a lunar year was 11 days shorter than a 365-day solar year. So, over time, the lunar calendar fell out of kilter with the solar year until, after a period of 19 years, they coincided once more. These extra days (up to a total of 30) are added to the lunar year to obtain the same date in the solar year. Once the total reaches 30, an extra lunar month is inserted into the calendar.
Unfortunately, 19 solar days * 11 lunar days = 209 which, when divided by 30, leaves a remainder of 29...which means there is still another day missing! Suffice to say that there are yet further calculations involving the insertion of embolistic months; increasing the allowable total of extra days to 31; the creation of so-called Golden Numbers; and the counting of dates from Jan 1st in Roman numerals backwards from XXX to I...I kid you not!!

Not forgetting to count every other month as only 29 days so the Roman numerals would start with XXIX but the 25th day of that month would be labelled both XXV and XXIV. Meanwhile, the day that was labelled XXVI would be re-dated as the 25th day of that month. All except the last days in December where the 26th and the 27th are always labelled XXV and XXIV respectively. The reasoning behind this bizarre method of renumbering the days of the year is that the new moon would always fall on the day whose Roman number was the same as the number of extra lunar days. So, if the number of extra days was 13, the new moon would fall on any day that was labelled XIII irrespective of whichever date it really was. Make sense? Perhaps not...especially as some years also happened to be golden number years for which the numbering system was entirely different.

Anyhow, whichever way you looked at it, calculating Easter was not only a complete dog's breakfast but it occupied a whole army of monks in the full-time calculation of the various tables each year. It also goes some way to explaining why Easter tends to wander around the calendar in such a haphazard fashion. And you thought selecting just one arbitrary date in 1973 was hairy?

* Except if the fourteenth day after the first full moon to occur after the vernal equinox was a Sunday, in which case Easter takes place on a different Sunday. Either the monks were introducing random rules to protect their nice little calculation jobs, or else they'd collectively lost the plot.

chacelp
February 9th, 2008, 03:20 AM
Anyhow, whichever way you looked at it, calculating Easter was not only a complete dog's breakfast but it occupied a whole army of monks in the full-time calculation of the various tables each year. It also goes some way to explaining why Easter tends to wander around the calendar in such a haphazard fashion. And you thought selecting just one arbitrary date in 1973 was hairy?

Touche my friend, touche

mlk
February 10th, 2008, 05:26 PM
post n# 20 (http://www.kirupa.com/forum/showpost.php?p=2283383&postcount=20) is officially the greatest post of the month.

glosrfc
February 11th, 2008, 07:45 PM
post n# 20 (http://www.kirupa.com/forum/showpost.php?p=2283383&postcount=20) is officially the greatest post of the month.

That all?? I demand nothing less than the K-Emmy award for most obtuse and undecipherable algorithm :to: