1

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)
}
2
  • Implement it yourself mutably. What is the signature? It's not actually clear how you expect the semantics to behave. Commented Aug 28 at 16:06
  • 2
    maps do not guarantee any order, so I'm not sure how much sense takeWhile will have here. If you want to, you can access the entries of the map, and takeWhile will be available there, so basically myMap.entries.takeWhile { it.key == "fizz" || it.value == "buzz"). Obviously use whatever predicate makes sense. Commented Aug 28 at 16:08

1 Answer 1

5

The iteration order of maps is ill-defined, and makes takeWhile make little sense. In addition, even if you wrote one, it would not work well for your use case, which is not applying takeWhile to any particular property of the keys and values but to a specific passage of time.

Frankly, explicit mutation is probably the right thing to do here for code clarity. If you must do something like this, consider doing it in lists and flattened collections first, using takeWhile appropriately, and then grouping into a map as part of the second phase after that.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.