loading...

Daily Challenge #54 - What century is it?

thepracticaldev profile image dev.to staff ・1 min read

Challenge
Write a function that will return an inputted numerical year in century format. The output should have the appropriate written ending ('st','nd','rd','th') as well.

Examples
In: 2259 Out: 23rd
In: 1124 Out: 12th
In: 2000 Out: 21st

Good luck!


This challenge comes from Cpt.ManlyPink on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

Discussion

pic
Editor guide
Collapse
brightone profile image
Oleksii Filonenko

A reasonably short and reasonably Rusty solution:

pub fn century(year: u32) -> String {
    let century = year / 100 + 1;
    let suffix = match century % 100 {
        11 | 12 | 13 => "th",
        _ => match century % 10 {
            1 => "st",
            2 => "nd",
            3 => "rd",
            _ => "th",
        },
    };
    format!("{}{}", century, suffix)
}
Collapse
citizen428 profile image
Michael Kohl

Sometimes Rust and F# really show their common ML roots, see my solution below.

Collapse
brightone profile image
Oleksii Filonenko

I used your answer as a cheatsheet of sorts. Good job! :)

Collapse
olekria profile image
Olek Ria

Garna robota!
Good job!

Collapse
brightone profile image
Oleksii Filonenko

Diakuyu :)
Thanks :)

Collapse
olekria profile image
Olek Ria

F#

let whatCenture (year: int) =
    let centure = year / 100 + 1

    let suffix x =
        if (centure % 13) = 12  || (centure % 13) = 11
        then "th"
        else match x % 10 with 
                | 1 -> "st"
                | 2 -> "nd"
                | 3 -> "rd"
                | _ -> "th"

    (string centure) + (suffix centure)

Collapse
citizen428 profile image
Michael Kohl

Always nice to see another F# solution :-)

Collapse
olekria profile image
Collapse
gnsp profile image
Ganesh Prasad

A bit of functional JS

const test = require('./tester');

const century = year => {
    if (isNaN(year)) return null;
    const nYear = Number(year);
    const cent = Math.floor(nYear / 100) + 1;
    const suffix = Math.floor(cent / 10) % 10 === 1 ? 'th'
        : cent % 10 === 1 ? 'st'
        : cent % 10 === 2 ? 'nd'
        : cent % 10 === 3 ? 'rd'
        : 'th';
    return `${cent}${suffix}`;
}

test(century, [
    {
        in: [2259],
        out: '23rd',
    },
    {
        in: [1124],
        out: '12th',
    },
    {
        in: [2000],
        out: '21st'
    },
    {
        in: [11092],
        out: '111th',
    },
]);
Collapse
savagepixie profile image
SavagePixie

I like your approach, it looks very clean.

This is probably too fringe to matter in most contexts, but wouldn't your function return 111st for the year 11,092?

Collapse
gnsp profile image
Ganesh Prasad

It would, indeed. Thanks for pointing out. Now I have fixed it and added a new test case.

OLD SOLUTION (Line 7)

const suffix = Math.floor(cent / 10) === 1 ? 'th'

UPDATED SOLUTION (Line 7)

const suffix = Math.floor(cent / 10) % 10 === 1 ? 'th'
Collapse
citizen428 profile image
Michael Kohl

F#:

module Century

let private parseYear s =
    let n = int s
    if n >= 0 then Ok n
    else Error "Year must be >= 0"

let private suffix c =
    match c % 100 with
    | 11
    | 12
    | 13 -> "th"
    | _ ->
        match c % 10 with
        | 1 -> "st"
        | 2 -> "nd"
        | 3 -> "rd"
        | _ -> "th"

let century (year : string) =
    match parseYear year with
    | Ok year ->
        let c = year / 100 + 1
        sprintf "%d%s" c (suffix c)
    | Error msg -> failwith msg

Failing on inputs < 0 was an arbitrary decision, it's easy to adopt the current code to take care of BC/AD.

Tests:

module CenturyTest

open FsUnit.Xunit
open Xunit
open Century

[<Fact>]
let ``2259``() = century "2259" |> should equal "23rd"

[<Fact>]
let ``1124``() = century "1124" |> should equal "12th"

[<Fact>]
let ``2000``() = century "2000" |> should equal "21st"

[<Fact>]
let ``0``() = century "0" |> should equal "1st"

[<Fact>]
let ``111``() = century "111" |> should equal "2nd"

[<Fact>]
let ``2245``() = century "2245" |> should equal "23rd"

[<Fact>]
let ``invalid year``() = (fun () -> century "-1" |> ignore) |> shouldFail
Collapse
savagepixie profile image
SavagePixie

Yes, yes, I know, 2000 should return "21st" century. I don't know of anyone who counts centuries like that, so my function returns them according to normal use.

const addBC = year => year < 0 ? " BC" : ""

const centurify = year => {
   const num = Math.ceil(Math.abs(year) / 100).toString()
   const suffix = num.match(/(11|12|13)$/)
      ? "th" : num.endsWith("1")
      ? "st" : num.endsWith("2")
      ? "nd" : num.endsWith("3")
      ? "rd" : "th"
   return num + suffix + addBC(year)
}
Collapse
citizen428 profile image
Michael Kohl

This may be missing a couple of special cases:

centurify(1124)
'12nd'
> centurify(1213)
'13rd'
Collapse
savagepixie profile image
SavagePixie

It's fixed now (I think). It should also support BC centuries.

Collapse
savagepixie profile image
SavagePixie

Oh, good catch

Collapse
suchafoka profile image
Suchafoka

python

def century (year):
    cent = str((year // 100) + 1)
    if int(cent) < 1:
        return "there isn't century"
    if cent[-1] == '1' :
        return cent+'st'
    elif cent[-1] == '2' and int(cent) != 12:
        return cent + 'nd'
    elif cent[-1] == '3':
        return cent + 'rd'
    else:
        return cent + 'th'
Collapse
choroba profile image
E. Choroba

This would've been even more interesting using the strict usage of "century" ;-)

#!/usr/bin/perl
use warnings;
use strict;

sub century {
    my ($year) = @_;
    my $century = 1 + int($year / 100);
    my $suffix;
    $suffix = 'th' if grep $century == $_, 11 .. 13;
    $suffix ||= {
        1 => 'st',
        2 => 'nd',
        3 => 'rd',
    }->{ substr $century, -1 } || 'th';
    $century . $suffix
}

use Test::More tests => 6;
is century(33),    '1st';
is century(2259), '23rd';
is century(1124), '12th';
is century(2000), '21st';
is century(3199), '32nd';
is century(2423), '25th';

First handle the exceptions (i.e. 11 - 13), then just use the last digit to decide.

Collapse
jay profile image
Jay

Rust:

fn century_name(year: u32) -> String {
    let century = (year / 100) + 1;
    format!(
        "{}{}",
        century,
        match century % 10 {
            _ if century > 10 && century < 14 => "th", // teen numbers exception
            1 => "st",
            2 => "nd",
            3 => "rd",
            _ => "th",
        }
    )
}
Collapse
dak425 profile image
Donald Feury

Insert Go Pun Here

century.go

package century

import (
    "strconv"
)

// Century gives the text representation of what century the given date belongs to
func Century(date int) string {
    // if date is negative (BC), convert to positive
    if date < 0 {
        date *= -1
    }
    prefix := date/100 + 1

    var suffix string

    // if its one the weird teens centuries, suffix is "th"
    switch prefix % 100 {
    case 11, 12, 13:
        suffix = "th"
    default:
        switch prefix % 10 {
        case 1:
            suffix = "st"
        case 2:
            suffix = "nd"
        case 3:
            suffix = "rd"
        default:
            suffix = "th"
        }
    }

    return strconv.Itoa(prefix) + suffix
}

century_test.go

package century

import "testing"

func TestCentury(t *testing.T) {
    testCases := []struct {
        description string
        input       int
        expected    string
    }{
        {
            "twenty third centry",
            2259,
            "23rd",
        },
        {
            "twelfth century",
            1124,
            "12th",
        },
        {
            "twenty first century",
            2000,
            "21st",
        },
        {
            "small centry",
            24,
            "1st",
        },
        {
            "odd centry name",
            1013,
            "11th",
        },
        {
            "large odd century name",
            11013,
            "111th",
        },
        {
            "negative century",
            -2000,
            "21st",
        },
    }

    for _, test := range testCases {
        if result := Century(test.input); result != test.expected {
            t.Fatalf("FAIL: %s - Centry(%d): %s - expected '%s'", test.description, test.input, result, test.expected)
        }
        t.Logf("PASS: %s", test.description)
    }
}

Collapse
chrisachard profile image
Chris Achard

Ah, this one was tricker than I thought because of the edge cases.

I choose a solution in JS that lists out all the endings in an object - but since they are almost all the same, maybe I should have done something else :)

Also, since I've started recording me solving these, you can check it out here: youtube.com/watch?v=ozws2mzhqkM

const centuryName = year => {
  const endings = {
    0: 'th',
    1: 'st',
    2: 'nd',
    3: 'rd',
    4: 'th',
    5: 'th',
    6: 'th',
    7: 'th',
    8: 'th',
    9: 'th',
  }

  const century = Math.floor(year / 100) + 1
  const rem = century % 10
  const ending = [11, 12, 13].includes(century % 100) 
    ? 'th' : endings[rem]
  return `${century}${ending}`
}
Collapse
cgty_ky profile image
Cagatay Kaya

A length Javascript solution, but I did not want to divide by 100.

const century = year => {
  const yearString = year.toString();
  const n = String(year).length;
  if (n - 3 < 0) {
    console.log("0th century");
  } else {
    console.log(digitYears(yearString, n));
  }
};

const digitYears = (yearString, n) => {
  const toIntAgain = parseInt(yearString[n - 3]) + 1;
  const edges = parseInt(yearString.slice(n - 4, n - 2)) + 1;
  if (edges == 11 || edges == 12 || edges == 13) {
    return `${yearString.slice(0, n - 3)}${toIntAgain}th century`;
  } else {
    const ending = endingDetermine(toIntAgain);
    return `${yearString.slice(0, n - 3)}${toIntAgain}${ending} century`;
  }
};

const endingDetermine = digit => {
  let ending = "";
  switch (digit) {
    case 1:
      ending = "st";
      break;
    case 2:
      ending = "nd";
      break;
    case 3:
      ending = "rd";
      break;
    default:
      ending = "th";
      break;
  }
  return ending;
};

Tried it with a few different years including the edge cases.

century(11034); //111th century
century(15134); //152nd century
century(16234); //163th century
century(942); //10th century
century(2042); //21st century
century(1342); //14th century
century(1242); //13th century
century(52); //0th century
Collapse
toanbku profile image