My Kotlin project needs to parse files and keep track of the outcome:
fun parseFiles(dp:MyDataProvider, files:List<String>)
{
//store the status of each processed file
val statusMap = mutableMapOf<String, MutableMap<String, List<ProcessingStatus>>>()
files.takeWhile{ currentFileNameToProcess ->
val statuses = processFile(currentFileNameToProcess)
//each group of files has a corresponding 'service' document
//serviceName() generates that group name based on the file name
statusMap.getOrPut(currentFileNameToProcess.serviceName(), ::mutableMapOf).putAll(mapOf( currentFileNameToProcess to statuses))
//This function will be part of an AWS lambda.
//To make sure we aren't running out of time,
//check the run time of the function against a threshold
!dp.thresholdExceeded.value()
}
}
I want to make this code conform to functional coding. I also want the map initialized with all the file names even if the loop ends early. I can do both by using groupBy() to transform my list of file names into a map before iterating:
files.groupBy{ it.serviceName() }
Kotlin Map<K,V> doesn't have a takeWhile() function. Since this function is run by AWS Lambda I use takeWhile() to exit the loop when a time threshold is reached to avoid timeout errors. Is there some other Kotlin function for iterating a map until a condition is detected?
Code without takeWhile():
internal enum class ProcessingStatus {
AlreadyProcessed, Sent, Pending, Error, Ignored
}
@Test
fun `test file mapping status`()
{
val fileNames = listOf("idoc_images_2050_131031_001_2324.zip","idoc_images_2050_131031_002_2324.zip", "idoc_images_2050_222031_001_2324.zip")
fun String.parseCbSeqNumber(): String {
return this.substring(24, 27)
}
fun String.serviceName():String
{
//we need to transform the name of the image file into a matching filename of the service file
//replace the 'images' substring with 'service' and strip out the three digit batch code
return this.replace("images", "service").replace(Regex("(.*)(_\\d{3}_)(.*.zip)$"), "$1_$3")
}
val serviceMap = fileNames.groupBy { it.serviceName() }.mapValues {
it.value.associateWith { listOf<ProcessingStatus>() }
}.mapValues { serviceEntry->
serviceEntry.value.mapValues {
fileEntry->
if(fileEntry.key.parseCbSeqNumber() == "001")
{
listOf(ProcessingStatus.Pending, ProcessingStatus.AlreadyProcessed)
}
else if(fileEntry.key.parseCbSeqNumber() == "002")
{
listOf(ProcessingStatus.Sent, ProcessingStatus.Ignored)
}
else{
listOf(ProcessingStatus.Error)
}
}
}
println(serviceMap)
assertThat(serviceMap.keys.size).isEqualTo(2)
}
myMap.entries.takeWhile { it.key == "fizz" || it.value == "buzz"). Obviously use whatever predicate makes sense.