Johann
Johann

Reputation: 29867

How to simplify this in Kotlin

Is there a shorter way in Kotlin to write the following code:

private fun getMonth(monthText: String): Int {
    var x = arrayOf("january", "february", "jumper").indexOf(monthText)

    if (x >= 0)
        return x

    x = arrayOf("Januari", "Februari", "Maret").indexOf(monthText)

    if (x >= 0)
        return x

    throw Exception("Not found")

}

I have to repeat the array search for many languages and want to avoid having to repeat duplicate code. Note: The returned value must indicate the month.

Upvotes: 0

Views: 179

Answers (6)

Gal Naor
Gal Naor

Reputation: 2397

I would do it like this:

private fun getMonth(monthText: String): Int {
   val a =  arrayOf("january", "february", "jumper")
   val b = arrayOf("Januari", "Februari", "Maret")

   val result = a.zip(b)
            .mapIndexed{idx, it -> if (it.first == monthText || it.second == monthText) idx else null }
            .mapNotNull{it}.firstOrNull()
   return result ?: throw Exception("Not found")
}

Upvotes: 0

Johann
Johann

Reputation: 29867

I think something like this would work (Note: For brevity I only listed 3 months for 2 languages. There are in fact 12 months for multiple languages):

private fun getMonth(monthText: String): Int {
    var x = arrayOf("january", "february", "march", "jumper", "Januari", "Februari", "Maret").indexOf(monthText)

    if (x < 0)
        throw Exception("Month not found")

    return (x % 12) + 1

}

Upvotes: 0

Roland
Roland

Reputation: 23242

I would probably hold some lists (or maps) containing your month texts and iterate over them, e.g.:

val englishMonths = sequenceOf("January", "February", "March" /*, ... */).mapIndexed { index, s -> s to index }.toMap()
val xxxMonths = sequenceOf("january", "february", "jumper"/*, ... */).mapIndexed { index, s -> s to index }.toMap()
// and all the other month mappings you need

Maybe you want to put those in an appropriate locale-mapping as well.

And your actual function would then just iterate over them:

fun getMonth(monthText : String) = sequenceOf(
  // all the month lists that are relevant:
  englishMonths, xxxMonths /*, ... */
)
  .mapNotNull { it[monthText] }
  .firstOrNull()

Which now would return null if no match was found. You can also just add ?: throw WhateverExceptionSuites() if you do not like a nullable type there.

Upvotes: 0

forpas
forpas

Reputation: 164054

With an array of arrays:

private fun getMonth(monthText: String): Int {
    val array = arrayOf(
        arrayOf("january", "february", "jumper"),
        arrayOf("Januari", "Februari", "Maret")
    )
    val months = array.firstOrNull { it.contains(monthText) }
    if (months != null) return months.indexOf(monthText)
    throw Exception("Not found")
}

The only inefficiency of this I can think of is that the array where monthText will be found, is scanned twice: once with contains() and then with indexOf().

Upvotes: 0

Dirk Bolte
Dirk Bolte

Reputation: 662

Maybe not the nicest way:

I first build a set with all possible mappings of month to index. I define the lists and process them with mapIndexed to get the index value. Afterwards I flatten those to get a set with Pair<String, Int>

val monthsToIndex by lazy {
        val lists = listOf(
                arrayOf("january", "february", "jumper"),
                arrayOf("Januari", "Februari", "Maret")
        );
        lists.map { it.mapIndexed { i, s -> s to i } }.flatten().toSet()
    };

The Search is now a find in that set + a null check to throw the exception:

private fun getMonth(monthText: String): Int {
        return monthsToIndex.find { it.first == monthText }?.second ?: throw Exception("Not found")
    }

A benefit of that approach would be a very concise place where you initialise all values - and only once.

Update:

You can also convert this into a map as Frank Neblung has suggested - this would definitely speed up the search:

val monthsToIndex by lazy {
    val lists = listOf(
        arrayOf("january", "february", "jumper"),
        arrayOf("Januari", "Februari", "Maret")
    );
    lists.map { it.mapIndexed { i, s -> s to i } }.flatten().toMap()
};

private fun getMonth(monthText: String): Int {
    return monthsToIndex[monthText] ?: throw Exception("Not found")
}

Upvotes: 1

Frank Neblung
Frank Neblung

Reputation: 3165

For performance reasons, you should better hash than search

private val monthByName = mapOf(
    "january" to 0,
    "february" to 1,
    "jumper" to 2,
    "Januari" to 0,
    "Februari" to 1,
    "Maret" to 2
)

private fun getMonth(monthText: String) = monthByName[monthText] ?: throw NoSuchElementException(monthText)

Upvotes: 4

Related Questions