I'm currently writing a Scala Object that converts time 24hr format (i.e. 00:00 - 23:59) into Time As Words (i.e. Eight o'clock, Half past eight, Quarter to nine etc).
How can I refactor the current ConvertTime function (Scala Object) into a more functional version using Pattern Matching?
I wanted to originally split out the function into smaller functions but in order to determine the correct case based on the expression they need to be kept within the following order to determine the correct time as words, by eliminating previous conditions.
I found an answer to the following post Multiple of Conditions by Daniel C Sobral that uses pattern matching. I'm not sure if the following approach could be addapted for my case. Any advice will be appreciated.
Python version of word lookup:
words_dict = {1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five', 6: 'six', 7: 'seven', 8: 'eight', 9: 'nine', 10: 'ten', 11: 'eleven', 12: 'twelve', 13: 'thirteen', 14: 'fourteen', 16: 'sixteen', 17: 'seventeen', 18: 'eighteen', 19: 'nineteen', 20: 'twenty', 21: 'twenty one', 22: 'twenty two', 23: 'twenty three', 24: 'twenty four', 25: 'twenty five', 26: 'twenty six', 27: 'twenty seven', 28: 'twenty eight', 29: 'twenty nine'}
Python version of if-elif-else conditions:
def time_conversion(words_dict, hours, minutes, period): """Return time as words based on relevant condition""" if hours == 12: hours2 = words_dict.get(1) else: hours2 = words_dict.get(hours+1) if hours == 12 and minutes == 0 and period == 'before midday': time_words = 'Midnight' elif hours == 12 and minutes == 0 and period == 'after midday': time_words = 'Midday' elif minutes == 0: time_words = "{0} o'clock {1}.".format(str(words_dict.get(hours)).title(), period) elif minutes == 15: time_words = "Quarter past {0} {1}.".format(words_dict.get(hours), period) elif minutes == 30: time_words = "Half past {0} {1}.".format(words_dict.get(hours), period) elif minutes == 45: time_words = "Quarter to {0} {1}.".format(hours2, period) elif minutes < 30: min_str = words_dict.get(minutes).capitalize() min_num = "" if minutes == 1 else "s" time_words = "{0} minute{1} past {2} {3}.".format(min_str, min_num, words_dict.get(hours), period) else: min_str = words_dict.get(60 - minutes).capitalize() min_num = "" if 60 - minutes == 1 else "s" time_words = '{0} minute{1} to {2} {3}.'.format(min_str, min_num, hours2, period) return time_words
Current Scala Object.
package time
/**
* Created by PeterW on 6/14/2017.
*/
object TimeAsWords extends App {
// Input string argument is based on 24hr format 23:59
val InputTime = args(0)
// regular expression string
val Exp24hr = """^(([01]\d|2[0-3]):([0-5]\d)|23:59)$"""
// a Map of words equivalent based on integer value
val WordsLookup = Map(1 -> "one", 2 -> "two", 3 -> "three", 4 -> "four", 5 -> "five",
6 -> "six", 7 -> "seven", 8 -> "eight", 9 -> "nine", 10 -> "ten",
11 -> "eleven", 12 -> "twelve", 13 -> "thirteen", 14 -> "fourteen",
16 -> "sixteen", 17 -> "seventeen", 18 -> "eighteen", 19 -> "nineteen",
20 -> "twenty", 21 -> "twenty one", 22 -> "twenty two", 23 -> "twenty three",
24 -> "twenty four", 25 -> "twenty five", 26 -> "twenty six", 27 -> "twenty seven",
28 -> "twenty eight", 29 -> "twenty nine")
// a Map of time periods
val PeriodLookup = Map(1 -> "before midday", 2 -> "after midday")
def TimeAsWords(InputTime: String, Exp24hr: String,
WordsLookup: Map[Int, String], PeriodLookup: Map[Int, String]) = {
def ExtractTime(InputTime: String, PeriodLookup: Map[Int, String]): (Int, Int, String) = {
// extract 12hours, minutes and period from input string
val HrsInt = InputTime.take(2).toInt
val Hrs12Int = 12 - ((-HrsInt % 12) + 12) % 12
val MinInt = InputTime.takeRight(2).toInt
val PdString = if (HrsInt < 12) PeriodLookup(1) else PeriodLookup(2)
Tuple3(Hrs12Int, MinInt, PdString)
}
def ConvertTime(WordsLookup: Map[Int, String], hours: Int, minutes: Int, period: String): String = {
// Convert Hours12, Minutes and Period into Time As Words
// using WordsLookup Map and conditional expressions, return the appropriate string
val HoursTo = if (hours == 12) 1 else hours + 1
val MinPastPlural = if (minutes > 1) "s" else ""
val MinToPlural = if (60 - minutes > 1) "s" else ""
val Midnight = "Midnight"
val Midday = "Midday"
val Oclock = String.format("%s o'clock %s.", WordsLookup(hours).capitalize, period)
val QuaterPast = String.format("Quarter past %s %s.", WordsLookup(hours), period)
val HalfPast = String.format("Half past %s %s.", WordsLookup(hours), period)
val QuarterTo = String.format("Quarter to %s %s.", WordsLookup(HoursTo), period)
lazy val MinPast = String.format("%s minute%s past %s %s.",
WordsLookup(minutes).capitalize, MinPastPlural, WordsLookup(hours), period)
lazy val MinTo = String.format("%s minute%s to %s %s.",
WordsLookup(60 - minutes).capitalize, MinToPlural, WordsLookup(HoursTo), period)
if(hours == 12 && minutes == 0 && period == "before midday"){
Midnight
} else if(hours == 12 && minutes == 0 && period == "after midday"){
Midday
} else if(minutes == 0){
Oclock
} else if(minutes == 15){
QuaterPast
} else if(minutes == 30){
HalfPast
} else if(minutes == 45){
QuarterTo
} else if(minutes < 30){
MinPast
} else{
MinTo
}
}
// validate format, convert to words else return error message
if (InputTime.matches(Exp24hr)) {
val (hours, minutes, period) = ExtractTime(InputTime, PeriodLookup)
val TimeString = ConvertTime(WordsLookup, hours, minutes, period)
println(TimeString)
} else {
println("Input String doesn't match required format: 00:00 - 23:59")
}
}
TimeAsWords(InputTime, Exp24hr, WordsLookup, PeriodLookup)
}