Home > Enterprise >  Kotlin combine lists based on common property
Kotlin combine lists based on common property

Time:11-28

I have two functions (GetPodsOne and GetPodsTwo) that return me a big csv string. I then do some processing to discard the part of the string I don't want. See snippet below.

var podValues = execGetPodsOne()
val testPodValuesLst: List<String> = podValues.split(",").map { it -> it.substringAfterLast("/") }
        
testPodValuesLst.forEach { it ->
 println("value from testPodList=$it")
        }
    
podValues = execGetPodsTwo()
val sitPodValuesLst: List<String> = podValues.split(",").map { it -> it.substringAfterLast("/") }
            
    sitPodValuesLst.forEach { it ->
 println("value from sitPodList=$it")
            }

This leaves me with two lists. See output of the above below:

value from testPodList=api-car-v1:0.0.118
value from testPodList=api-dog-v1:0.0.11
value from testPodList=api-plane-v1:0.0.36

value from sitPodList=api-car-v1:0.0.119
value from sitPodList=api-dog-v1:0.0.12
value from sitPodList=api-plane-v1:0.0.37

What i would like to do is end up with the objects inside a data class like below:

data class ImageVersions(val apiName: String, val testPodVersion: String, val sitPodVersion: String)

api-car-v1, 0.0.118, 0.0.119
api-dog-v1, 0.0.11, 0.0.12
api-plane-v1, 0.0.36, 0.0.37

I've used test and sit above but I'm going to have maybe another 5 environments eventually. Looking for a nice way to get the versions for each api and easily combine into that ImageVersions data class.

thanks

CodePudding user response:

Considering that you're going to have maybe another 5 environments eventually, I tried to write something that will scale well:

enum class Env { Test, Sit }

data class ImageVersions(val apiName: String, val versions: Map<Env, String?>)

fun String.getNameAndVersion() = substringBefore(':') to substringAfter(':')

fun getVersions(envMap: Map<Env, List<String>>): List<ImageVersions> {
    val envApiNameMap = envMap.mapValues { it.value.associate(String::getNameAndVersion) }
    val allApiNames = envApiNameMap.flatMap { it.value.keys }.distinct()
    return allApiNames.map { apiName ->
        ImageVersions(apiName, envApiNameMap.mapValues { it.value[apiName] })
    }
}

Playground example

  • So instead of separate val testPodVersion: String, val sitPodVersion: String, here you have a map. Now the structure of ImageVersions always remains the same irrespective of how many environments you have.
  • getNameAndVersion is a helper function to extract apiName and version from the original string.
  • getVersions accepts a list of versions corresponding to each environment and returns a list of ImageVersions
  • envMap2 is same as envMap just that the list is now a map of apiName and its version.
  • allApiNames contains all the available apiNames from all environments.
  • Then for every apiName, we take all the versions of that apiName from all the environments.

In future, if your have another environment, just add it in the Env enum and pass an extra map entry in the envMap of getVersions. You need not modify this function every time you have a new environment.

CodePudding user response:

How about this:

val testPodValuesMap = testPodValuesLst.associate { it.split(':').zipWithNext().single() }
val sitPodValuesMap = sitPodValuesLst.associate { it.split(':').zipWithNext().single() }

val mergedMap = (testPodValuesMap.keys   sitPodValuesMap.keys).associateWith { key ->
    testPodValuesMap.getValue(key) to sitPodValuesMap.getValue(key)
}

val imageVersions = mergedMap.map { (k, v) -> ImageVersions(k, v.first, v.second) }
println(imageVersions.joinToString("\n"))

which prints

ImageVersions(apiName=api-car-v1, testPodVersion=0.0.118, sitPodVersion=0.0.119)
ImageVersions(apiName=api-dog-v1, testPodVersion=0.0.11, sitPodVersion=0.0.12)
ImageVersions(apiName=api-plane-v1, testPodVersion=0.0.36, sitPodVersion=0.0.37)

CodePudding user response:

As a first step I would extract the apiNames from both lists:

val apiNames = list1.map { it.replace("value from ", "").split("[=:]".toRegex())[1] }
  .plus(list2.map { it.replace("value from ", "").split("[=:]".toRegex())[1] })
  .distinct()

Then I'd create the ImageVersions instances by looping over apiNames:

val result = apiNames
  .map { apiName ->
    ImageVersions(
      apiName,
      (list1.firstOrNull { it.contains(apiName) } ?: "").split(":")[1],
      (list2.firstOrNull { it.contains(apiName) } ?: "").split(":")[1]
    )
  }
  .toList()

The reason to first extract the apiNames is, that apiNames missing in one of the two lists will still end up in the final result.

Kotlin Playground

  • Related