|
// In a nutshell, there are three ways to solve this problem: the "naive" one, the "normal" one, and the "crazy" one. |
|
|
|
// What a novice and naive programmer, not so well-familiarized with mathematics, would probably achieve: |
|
const naiveStndth = n => { |
|
if((n <= -10 && n > -14) || (n >= 10 && n < 14)) |
|
return "th" |
|
|
|
let lastDigit = Math.trunc(n % 10) // Welp... Is this really the approach from a novice programmer? I'm not so sure. |
|
|
|
if(lastDigit == 1 || lastDigit == -1) |
|
return "st" |
|
if(lastDigit == 2 || lastDigit == -2) |
|
return "nd" |
|
if(lastDigit == 3 || lastDigit == -3) |
|
return "rd" |
|
|
|
return "th" |
|
} |
|
|
|
|
|
// What a more experienced programmer would achieve, with |
|
// an one-liner (ab)using the JS quirks regarding bitwise truncation |
|
// and _in situ_ calculation of array indexing |
|
// (not so otherworldly, tho): |
|
const normalStndth = n => |
|
["st","nd","rd","th"][(((Math.abs(n)|0)/10)|0)==1?3:Math.min(3,(((Math.abs(n)|0)%10)+9)%10)] |
|
|
|
// What a person without proper socialization skills, |
|
// lonely dealing with computers in a daily basis since their 8s, |
|
// just like myself, would achieve: |
|
const crazyStndth = n => { |
|
/* |
|
# Reasons |
|
- I don't wanna use strings. |
|
- I don't wanna KISS (Keep It Simple and Steady) |
|
- I have no close friends, let alone relationships. |
|
- The only "love" I know is a love for deep abstract concepts, such as hidden and dark concepts (e.g. cryptography and seeking pattern at the most ineffable places), math, randomness and entropy, philosophy, and so on. The ordo ab chao is fascinating. |
|
- Everything in the cosmos has some mathematical pattern, even though humans haven't discovered it yet (here I'm nodding to the Theory of Everything) |
|
- I stopped important meds for treating depression (yeah, thank you, unemployment!), so I'm forced to seek something "costless" to cope with the burdens of my existence while keeping _these thoughts_ away from me. |
|
|
|
= Crazy logic breakdown = |
|
Read the-breakdown-of-logic.md for a better (Markdown) version |
|
|
|
Possible suffixes and their respective letter positions: |
|
Map the possible suffixes to their letter positions within the alphabet (A=1, B=2, and so on). |
|
|
|
m AB ( A, B) |
|
0 st (19, 20) |
|
1 nd (14, 4) |
|
2 rd (18, 4) |
|
3 th (20, 8) |
|
|
|
== Logic behind B (the second letter) == |
|
|
|
Table: flow of thought |
|
m B ? (?-20)/4 (m+1)/2 -> flooring |
|
0 20 20 0 0.5 0 |
|
1 04 24 1 1.0 1 |
|
2 04 24 1 1.5 1 |
|
3 08 28 2 2.0 2 |
|
|
|
(? denotes "a temporary something", as my flow of thought goes from left to right columns) |
|
|
|
What I want to achieve: |
|
B : {0, 1, 2, 3} -> {20, 4, 4, 8} |
|
|
|
How to acheve it: |
|
B(m) = 20 {if m = 0} |
|
⌊(m + 1) ÷ 2⌋ × 4 {otherwise} |
|
|
|
(⌊x⌋ denotes the floor(x) operation) |
|
|
|
Incremental steps: |
|
1. Add 1 (-> {1, 2, 3, 4}) |
|
2. Halve (-> {0.5, 1.0, 1.5, 2.0}) |
|
3. Round down (-> {0, 1, 1, 2}) |
|
4. Multiply by 4 (-> {0, 4, 4, 8}) |
|
5. Null-coalesce 20 (so, if it's a falsy result, return 20) (-> {20, 4, 4, 8}) |
|
|
|
== Logic behind A (the first letter) == |
|
It's a tad complexier than the second letter... But it's achievable. |
|
|
|
Table: flow of thought |
|
(Odd) (Even) |
|
m A A_2 A/2 A/2-6 ? m eq 0 18+(m eq 0) |
|
0 19 00010011 - - 19 1 19 |
|
1 14 00001110 7 1 - - - |
|
2 18 00010010 - - 18 0 18 |
|
3 20 00010100 10 4 - - - |
|
/\ |
|
Hey! It's the Squares (1,4,9,16,...) |
|
|
|
(? denotes "a temporary something", as my flow of thought goes from left to right columns) |
|
(I wrote down the binary version of the expected number just so I could visualize possible patterns hidden within it) |
|
|
|
What I want to achieve: |
|
A : {0, 1, 2 3} -> {19, 14, 18, 20} |
|
|
|
How to achieve it: |
|
A(m) = (1 - (m mod 2)) × odd_factor + (m mod 2) × even_factor |
|
|
|
Coefficient for odd_factor: 1 when m ∈ {1,3}, 0 otherwise |
|
Coefficient for even_factor: 1 when m ∈ {0,2}, 0 otherwise |
|
|
|
odd_factor = ((⌊m ÷ 2⌋ + 1)² + 6) × 2 |
|
(⌊x⌋ denotes the floor(x) operation) |
|
|
|
Incremental steps when m is odd (to calculate odd_factor): |
|
1. Halve (-> {0.5, 1.5}) |
|
2. Round down (-> {0, 1}) |
|
3. Add 1 (-> {1, 2}) |
|
4. Square (-> {1, 4}) |
|
5. Add 6 (-> {7, 10}) |
|
6. Double (-> {14, 20}) |
|
|
|
even_factor = 18 + (m eq 0) |
|
(eq denotes a binary operator which returns 1 when their operands are equal, 0 otherwise) |
|
|
|
Incremental steps when m is even (to calculate even_factor): |
|
1. Conditional equal to zero (-> {true, false}) |
|
2. Type-coerce to number (-> {1, 0}) |
|
3. Add 18 (-> {19, 18}) |
|
|
|
Incremental steps for every m: |
|
1. Add both the following values described as i and ii: |
|
i. Multiply the even bit of m (m&1) with even_factor |
|
ii. Multiply 1 - the even bit of m (1-(m&1)) with odd_factor |
|
|
|
== Final logic == |
|
Now, A is the position within the alphabet for the first letter of the suffix and B is the position within the alphabet for the second letter of the suffix. |
|
Considering that 97 is the ASCII code for the first lowercase letter, which has the position 1 within the alphabet, just add 96 to the values of a and b (so 1->97, 2->98 and so on) to get their ASCII codes. |
|
Finally, use String.fromCharCode to construct a string out of both ASCII codes, which will be naturally concatenated. |
|
|
|
That's it. I hope you got to understand my (crazy?) way of thinking. |
|
*/ |
|
let m = (((Math.abs(n)|0)/10)|0)==1 ? 3 : Math.min(3, (((Math.abs(n)|0) % 10) + 9) % 10) |
|
let b = (((((1 + m) / 2)|0) * 4) || 20) |
|
let a = ((1 - m&1)*18 + (+(m==0)) + ((m&1) * ((1 + m/2|0)**2 + 6))*2) |
|
return String.fromCharCode(...[a+96, b+96]) |
|
}; |
|
|
|
// Testing from -33 to 33: |
|
for(let i = -33; i <= 33; i++){ |
|
let naive = i.toString() + naiveStndth(i) |
|
let normal = i.toString() + normalStndth(i) |
|
let crazy = i.toString() + crazyStndth(i) |
|
|
|
log(i, {naive, normal, crazy}) // Every approach resulting in the same output |
|
} |