2
\$\begingroup\$

I have the following kotlin function that takes in a string "value", then attempts to parse "value" as both a LocalDateTime and an Int. It returns a Pair of the parsed value of "value" and an enum that specifies which of the three types it was determined to be.

enum class RangeValueType{
    Int,
    Date,
    String
}

fun parseValue(value: String): Pair<RangeValueType, Any> {
    val rangeDate: LocalDateTime
    val rangeInt: Int
    //try date first
    try {
        //ISO_LOCAL_DATE_TIME e.g. 2011-12-03T10:15:30 for Dec. 3rd, 2011 & 10:15 30 sec.
        //can't use basic date b/c e.g. 20111203 is indistinguishable from an int.
        rangeDate = LocalDateTime.parse(value)
        //it parsed to a date, so return the date
        return Pair(RangeValueType.Date, rangeDate)
    }catch (dtpe: DateTimeParseException) {
        //not a date
    }
    //try number next
    try{
        rangeInt = value.toInt()
        //it parsed to an int, so return the int
        return Pair(RangeValueType.Int, rangeInt)
    }catch (nfe: NumberFormatException)
    {
        //not an int
    }
    //not date or int, must be a string
    return Pair(RangeValueType.String, value)
}

My principal concern is that I am using caught exceptions as a means of flow control, which I understand to generally be a Bad Idea. I know C# has a "TryParse" method for exactly this kind of case, but I wasn't able to find anything for Kotlin / Java.

Is there a better way to parse the values into their proper types?

\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

(Disclamer: I only know Kotlin theoretically)

You can avoid the second try/catch by using String.toIntOrNull instead of String.toInt.

And by extracting the LocalDateTime conversion into a separate function, that also returns null when it fails (which also would help fulfill the single responsibility principle), you could then use a functional approach with the safe call operator (?.), the Elvis operator (?:) and the run extension method:

enum class RangeValueType {
    INT,
    DATE,
    STRING
}

fun parseLocalDateTime(value: String): LocalDateTime? {
    try {
        return LocalDateTime.parse(value)
    } catch (dtpe: DateTimeParseException) {
        return null;
    }
}

fun parseValue(value: String): Pair<RangeValueType, Any> {
    return parseLocalDateTime(value)?.run { Pair(RangeValueType.DATE, this) } ?:
             value.toIntOrNull()?.run { Pair(RangeValueType.INT, this) } ?: 
             value.run { Pair(RangeValueType.STRING, this) } 
}

Runable version

(NB: enum constants should be written all capitalized so they are not confused with types.)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.