DEV Community

dev.to staff
dev.to staff

Posted on

Daily Challenge #296 - Years to Centuries

The first century spans from the year 1 up to and including the year 100, The second - from the year 101 up to and including the year 200, etc. Return the century of the input year.

Examples

"1999" --> "20th"
"2011" --> "21st"
"2154" --> "22nd"
"2259" --> "23rd"
"1124" --> "12th"
"2000" --> "21st"
"20000" --> "210th"

Tests

8120
30200
1601
2020
3030
1900
1776

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!

Top comments (20)

Collapse
 
soorajsnblaze333 profile image
Sooraj (PS)
const toCentury = (year) => {
  const century = Math.ceil(year / 100);
  if (century.toString().length > 2) return year + " --> " + century + "th";
  switch(century % 10) {
    case 1: return year + " --> " + century + "st";
    case 2: return year + " --> " + century + "nd";
    case 3: return year + " --> " + century + "rd";
    default: return year + " --> " + century + "th";
  }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
alfredosalzillo profile image
Alfredo Salzillo

Clean and elegant!

Collapse
 
mellen profile image
Matt Ellen-Tsivintzeli • Edited

"1066 --> 11st" should be 11th
"10266 --> 103th" should be 103rd

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
soorajsnblaze333 profile image
Sooraj (PS)

But according to the math, 1 - 100 is 1st century. So I thought 1901 - 2000 is 20th century and 2001 - 2100 is 21st century. Is my understanding wrong ?

Collapse
 
_bkeren profile image
''

This question has conflicts.

"20000" --> "210th" (should it be 201st?)

In the question:

"The first century spans from the year 1 up to and including the year 100, The second - from the year 101 up to and including the year 200, etc. Return the century of the input year"

but toCentury(2000) -> 21st?

Collapse
 
mellen profile image
Matt Ellen-Tsivintzeli

Good question. I didn't notice that in the test cases.

Collapse
 
mrwebuzb profile image
Asliddinbek Azizovich • Edited

Here is my golang solution


package main

import (
    "fmt"
)

func getSuffix(number int) string {
    if number%100 >= 11 && number%100 <= 13 {
        return "th"
    }
    switch number%10 {
        case 1:
            return "st"
        case 2:
            return "nd"
        case 3:
            return "rd"
    }

    return "th"
}

func yearToCentury(year int) string {
    century := year / 100

    return fmt.Sprintf("%d%s century", century+1, getSuffix(century+1))
}

func main() {
    tests := []int{1999, 2011, 2154, 2259, 1124, 2000, 20000}

    for _, year := range tests {
        fmt.Printf("%d --> %s\n", year, yearToCentury(year))
    } 
}

Enter fullscreen mode Exit fullscreen mode
Collapse
 
mellen profile image
Matt Ellen-Tsivintzeli

what about the year 11122?

Collapse
 
mrwebuzb profile image
Asliddinbek Azizovich • Edited

I think, it will be 112nd century, because, current, 2020 year is inside of 21st century :)

Thread Thread
 
mellen profile image
Matt Ellen-Tsivintzeli

It should be 112th not 112nd, because 12 gets a th

Thread Thread
 
mrwebuzb profile image
Asliddinbek Azizovich

ohhhh, it was grammatically mistake, sorry

Collapse
 
cipharius profile image
Valts Liepiņš

Solution in Haskell:

import Numeric.Natural (Natural)

toCentury :: Natural -> String
toCentury = suffix . ceiling . (/ 100) . (+ 1) . fromIntegral
  where
    suffix x
     | x `mod` 10 == 1 = show x <> "st"
     | x `mod` 10 == 2 = show x <> "nd"
     | x `mod` 10 == 3 = show x <> "rd"
     | otherwise       = show x <> "th"
Collapse
 
willsmart profile image
willsmart • Edited

Here's a typescript implementation, and a ✨verbose-and-pretty✨ reference to check it against.
It's funny, but I take much more care with these little easy tasks than I used to. There's always more nuance than you expect and the implementations end up sitting at the bottom of your library being used for everything.

(btw, I tend to use ~~ as an alternative to of Math.floor because it's more succinct, and most often a little quicker)

function suffixForOrdinal(ordinal: number): string {
  return (~~(ordinal / 10) % 10 !== 1 && ['th', 'st', 'nd', 'rd'][ordinal % 10]) || 'th';
}

function nameOfCentury(yearOrdinal: string | number): string {
  const centuryOrdinal = ~~((+yearOrdinal + 99) / 100);
  return `${centuryOrdinal}${suffixForOrdinal(centuryOrdinal)}`;
}

I'll check my working by comparing with a naive implementation that's more readable and hopefully reliable out of the gate...

// Stringifying the ordinal is an easier way to get individual the parts of the year...
function suffixForOrdinal_slowButSure(ordinal: number): string {
  const ordinalString = String(ordinal),
    tens = +ordinalString.slice(-2, -1),
    ones = +ordinalString.slice(-1);
  if (tens == 1) return 'th'; // i.e. 11th, not 11st
  if (ones == 1) return 'st'; // 81st
  if (ones == 2) return 'nd'; // 82nd
  if (ones == 3) return 'rd'; // 83rd
  return 'th'; // anything else is 'th'. 45th
}

function nameOfCentury_slowButSure(yearOrdinal: string | number): string {
  // When extracting the century we need to be working with the index, not ordinal.
  // That's why the weirdness about 2000 -> 20th, 2001 -> 21st
  //    (The year 2000 as an ordinal has index 1999)
  const yearIndex = +yearOrdinal - 1,
    yearIndexString = String(yearIndex),
    centuryIndex = +yearIndexString.slice(0, -2) || 0,
    centuryOrdinal = centuryIndex + 1;
  return `${centuryOrdinal}${suffixForOrdinal_slowButSure(centuryOrdinal)}`;
}


// Now do some 'testing' by comparing the implementations
// This ensures that either both are right or both are wrong,
//   I'm pretty sure that the verbose one is as right as any static fixtures I could make.
(function hareVsTortoise(startYearOrdinal = 1, endYearOrdinal = 100000) {
  for (let yearOrdinal = startYearOrdinal; yearOrdinal <= endYearOrdinal; yearOrdinal++) {
    if (nameOfCentury(yearOrdinal) !== nameOfCentury_slowButSure(yearOrdinal)) {
      console.error(
        `Conflicting answers for year ${yearOrdinal}:\n      Hare: "${nameOfCentury(
          yearOrdinal
        )}"\n  Tortoise: "${nameOfCentury_slowButSure(yearOrdinal)}"`
      );
      return;
    }
  }
  console.log(`Checked ${endYearOrdinal + 1 - startYearOrdinal} years, all seems good`);
})();

/*-->
Checked 100000 years, all seems good
[1, 99, 100, 101, 200, 300, 400, 1000, 1100, 1200, 1300, 1400, 1900, 2000, 2001, 2020, 10000, 10100].forEach(year => {
  console.log(`Year ${year} --> ${nameOfCentury(year)}`);
});

/*-->
Year 1 --> 1st
Year 99 --> 1st
Year 100 --> 1st
Year 101 --> 2nd
Year 200 --> 2nd
Year 300 --> 3rd
Year 400 --> 4th
Year 1000 --> 10th
Year 1100 --> 11th
Year 1200 --> 12th
Year 1300 --> 13th
Year 1400 --> 14th
Year 1900 --> 19th
Year 2000 --> 20th
Year 2001 --> 21st
Year 2020 --> 21st
Year 10000 --> 100th
Year 10100 --> 101st
Collapse
 
matrossuch profile image
Mat-R-Such

Py <3

def what_century(year):
    c, r = divmod(year,100)

    if r > 0:   c += 1

    #name 

    if c in [11,12,13]:         return str(c)+'th'
    elif str(c)[-1] == '1':     return str(c)+'st'
    elif str(c)[-1] == '2':     return str(c) + 'nd'
    elif str(c)[-1] == '3':     return str(c) + 'rd'
    else:                        return str(c) + 'th'
Enter fullscreen mode Exit fullscreen mode
Collapse
 
mellen profile image
Matt Ellen-Tsivintzeli
function thuffix(n)
{
  let ending = 'th';
  if(n%100 != 11 && n%100 != 12 && n%100 != 13)
  {
    if(n%10 == 1)
    {
      ending = 'st';
    }
    else if(n%10 == 2)
    {
      ending = 'nd';
    }
    else if(n%10 == 3)
    {
      ending = 'rd';
    }
  }
  return ending;
}

function getCentury(year)
{
  let c = Math.floor(year/100);
  if(year%100 != 0)
  {
    c++;
  }
  return `${c}${thuffix(c)}`;
}
Collapse
 
developman profile image
Dmitry Mineev

The ruby example

def ordinalize(number)
  case number % 10
    when 1
      "st"
    when 2
      "nd"
    when 3
      "rd"
    else
      "th"
  end
end

def test_years(years_array)
  years_array.each do |year|
    correction = 0
    if year % 100 == 0
      correction = 1
    end
    result = (year/100.0).ceil + correction
    puts "#{year} --> #{result}#{ordinalize(result)}"
  end
end

puts "Example scope:"
test_years([1999, 2011, 2154, 2259, 1124, 2000, 20000])
puts "Test scope:"
test_years([8120, 30200, 1601, 2020, 3030, 1900, 1776])

And one-line style with activesupport gem installed

 [{name: "Example", years: [1999, 2011, 2154, 2259, 1124, 2000, 20000]},
{name: "Test", years: [8120, 30200, 1601, 2020, 3030, 1900, 1776]}].
each{ |scope| puts "#{scope[:name]} scope:"; scope[:years].
each{ |year| year % 100 == 0 ? correction = 1 : correction = 0;
result = (year/100.0).ceil + correction;
puts "#{year} --> #{result.ordinalize}" }}
Collapse
 
3limin4t0r profile image
3limin4t0r • Edited

This might be a bit over-engineered, but here is another Ruby solution:

def to_century(year)
  (year.to_i / 100 + 1).english.ordinal
end

class Integer
  def english
    English::Integer.new(self)
  end
end

module English
  class Integer    
    def initialize(integer)
      @value = integer
    end

    ##
    # Returns ordinal notation of the current integer ("-2nd", "1st", "312th").
    def ordinal
      "#{@value}#{ordinal_suffix}"
    end

    ##
    # Returns the ordinal suffix of the current integer ("st", "nd", "rd", "th").
    def ordinal_suffix
      case @value.abs % 100
      when 1, 21, 31, 41, 51, 61, 71, 81, 91 then 'st'
      when 2, 22, 32, 42, 52, 62, 72, 82, 92 then 'nd'
      when 3, 23, 33, 43, 53, 63, 73, 83, 93 then 'rd'
                                             else 'th'
      end
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Which produces the following output:

years = %w[8120 30200 1601 2020 3030 1900 1776]
years.to_h { |year| [year, to_century(year)] }
#=> {"8120"=>"82nd", "30200"=>"303rd", "1601"=>"17th", "2020"=>"21st", "3030"=>"31st", "1900"=>"20th", "1776"=>"18th"}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
thepeoplesbourgeois profile image
Josh • Edited
defmodule Year do
  @doc """
  Returns the stringified, suffixed form of a numeric year.

  Use `to_century/1` if you need to do further numeric work on the
  century number before (or in lieu of) using it as a string.

  ## Examples

      iex> Year.centurify(8120)
      "82nd"
      iex> Year.centurify(30200)
      "303rd"
      iex> Year.centurify(1601)
      "17th"
      iex> Year.centurify(2020)
      "21st"
      iex> Year.centurify(3030)
      "31st"
      iex> Year.centurify(1900)
      "20th"
      iex> Year.centurify(1776)
      "18th"
  """
  def centurify(year) do 
    century = to_century(year)
    "#{century}#{suffix(century)}"
  end

  def to_century(year), do: div(year, 100) + 1

  defp suffix(number) when number >= 100, 
    do: number |> rem(100) |> suffix
  defp suffix(number) when number in 11..13, do: "th"
  defp suffix(number) do
    case rem(number, 10) do
      1 -> "st"
      2 -> "nd"
      3 -> "rd"
      _ -> "th"
    end
  end
end

defmodule YearTest do
  import ExUnit.Case
  doctest Year
end
Enter fullscreen mode Exit fullscreen mode