Kotlin iterate through everyday in this year

Written by elye.project | Published 2017/10/08
Tech Story Tags: android | kotlin | mobile-app-development | androiddev | android-app-development

TLDRvia the TL;DR App

The below `everyday in this year 2017` is not a pseudocode, but a real Kotlin code!! So natural isn’t it?

for (everyday in this year 2017) {
     // do some with everyday
}

It is actually an iterator that iterates through through custom Date type. I’ll explain how it is done below… enjoy…

Defining this year 2017

In Kotlin, we have something called infix for a function, which allow one to omit the . and parenthesis () when calling a function of a class.

The above code of this year 2017 is actually a function with the name year that received an integer argument 2017. The function is defined within a class (represented by this). It supposed to look like this.year(2017). But it is declared with infix as below within a class. Hence we can change it to this year 2017.

private infix fun year(year: Int)

If is unfortunate we can’t omit the this keyword in this case. By definition, infix call has to goes in between the receiver class and it’s argument.

What does the function do?

Well, the function is as below. It returns a range of dates from Jan 1 till Dec 31.

private infix fun year(year: Int): DateRange {
    return Date(year, 1, 1)..Date(year, 12, 31)
}

The .. is another cool operator that define a range of two value. How could you achieved this? It is explained in my blog in Amused by .. and in operators in Kotlin.

Defining everyday

The everyday is a Custom Date type variable that I setup as per my previous blog.

class Date (val year: Int, val month: Int, val day: Int) 
     : Comparable<Date> {

    override operator fun compareTo(other: Date): Int {
        // Refers to the blog for the comparison detail
       return 0
    }

    operator fun rangeTo(that: Date) = DateRange(this, that)
}

However, to make it slightly better, let’s ensure the Date can only contain validate date.

Days of each month

To do that, let’s start by defining the valid total days of each month. I add a private function for that purpose as below. Here I use the wonderful when operator of Kotlin. (Look how concise the code is!)

private fun daysInMonth(month: Int, year: Int): Int {
    return when (month) {
        4, 6, 9, 11 -> 30
        2 -> if (leapYear(year)) 29 else 28
        else -> 31
    }
}

Not forgetting in a leap year, we have 29 days in February. So to know a leap year or not, another private function is added as below

private fun leapYear(year: Int) = year % 4 == 0

Date validation

Now we have dayInMonth function, we could now validate a given date during it’s construction by setting it in the init function.

companion object {
    const val MONTHS_IN_A_YEAR = 12
}

init {
    if (month > MONTHS_IN_A_YEAR || month <  0) {
        throw IllegalStateException
           ("Month must between 1 - $MONTHS_IN_A_YEAR")
    }
    if (day > daysInMonth(month, year)) {
        throw IllegalStateException
           ("Day $day not valid in month $month of year $year")
    }
}

Iterating Date

We now have the everyday and this year 2017 for

everyday in this year 2017 

The missing part is the in operator. Note this in is different from the contains operator in that is defined in my previous blog. The in here is an operator that works on the iterable object.

Making DateRange Iterable

Given that the in operator is used on the DateRange, we have to make the DateRange an Iterable<Date> object. The Iterable needs to overload iterator() operator as below. The overload function just return the DateIterator<Date> that enables the one to iterate through the Dates

class DateRange(override val start: Date, 
    override val endInclusive: Date)
    : ClosedRange<Date>, Iterable<Date> {

    override fun iterator(): Iterator<Date> {
        return DateIterator(start, endInclusive)
    }
}

Well, we don’t have the DateIterator<Date> class defined yet, let’s do it below.

Defining DateIterator Type

The DateIterator, has to implement from the Iterator interface (with the Date type). It will have to override the hasNext and next function of Iterator with the appropriate operators on Date object.

class DateIterator(start: Date, private val endInclusive: Date)
    : Iterator<Date> {
    
    private var current = start

    override fun hasNext(): Boolean {
        return current <= endInclusive
    }

    override fun next(): Date {
        return current++
    }
}

The operators overloading

The comparator operator (i.e. <= operator) overload has been defined in my previous blog.

However, we still miss out the inc operator i.e. ++. Let’s define it under the Date class as below (again the powerful when).

operator fun inc(): Date {
    return when {
        (day < daysInMonth(month, year)) 
             -> Date(year, month, day+1)
        (month < MONTHS_IN_A_YEAR) 
             -> Date(year, month + 1, 1)
        else -> Date(year + 1, 1, 1)
    }
}

All Done…

With the above, you could now use the below to iterate through date within a given year easily, with such a humanly readable code :)

for (everyday in this year 2017) {
    // do some with everyday
}

Check out the complete code at https://gist.github.com/elye/5c107e9c8e9121d785f4ddfa103f5808

If you like my post and want to get future update, do click 👏👏👏, follow me on medium, Twitter or Facebook


Published by HackerNoon on 2017/10/08