Home > front end >  How can I use elegant kotlin techniques to solve this problem? (FP)
How can I use elegant kotlin techniques to solve this problem? (FP)

Time:11-29

I have a kotlin problem, can you come up with a elegant way of solving it?

so effectively I have a list of Journeys which could be by car or not, I want to find where there are consecutive car journeys in a list; i.e. 2 or more cars in a row in the list. Then from these chains of consecutive cars I wan to sum the mileage per chain and print out the maximum. This is the data class

  data class Journey(
    val isCar: Boolean = false,
    val mileage: Int,
  )

Here is an example list of journeys. So in this example the correct answer should be 80, because there are two consecutive entries with 40 40 as the mileage. That beats the only other consecutive entry of 10 20=30. The rest are single car journeys and non car journeys so are ignored.

listOf(
      Journey(true, 10),
      Journey(true, 20),
      Journey(false, 105),
      Journey(true, 1046),
      Journey(false, 130),
      Journey(true, 40),
      Journey(true, 40),
    )

My solution is below, however it's very ugly and non-functional. Can you solve this in an elegant way using modern functional techniques without mutable Lists? Thanks

val journeys = listOf(
      Journey(true, 10),
      Journey(true, 20),
      Journey(false, 105),
      Journey(true, 1046),
      Journey(false, 130),
      Journey(true, 40),
      Journey(true, 40),
    )

    val cars = journeys.filter { it.isCar }
    val journeysByIndex = cars.map { journeys.indexOf(it) to it  }
    val consecutiveJourneys = mutableListOf<MutableList<Journey>>()

    journeysByIndex.forEach {
      if (consecutiveJourneys.any { cj1 -> cj1.any { cj2 -> cj2 == it.second  } }) {
        return@forEach
      }
      val journeyList = mutableListOf(it.second)
      var index = it.first
      var nextJourneyExists = true
      while (nextJourneyExists) {
        var journey: Journey? = null
        if ((index   1) < journeys.size  && journeys[index   1].isCar) {
          journey = journeys[index 1]
        }
        nextJourneyExists = if (journey!= null) {
          journeyList.add(journey)
          true
        } else {
          false
        }
        index  = 1
      }
      consecutiveJourneys.add(journeyList)
    }

    var maxAmountForConsecutive = consecutiveJourneys.filter {it.size > 1 }.map{ it.sumOf {cs -> cs.mileage}}.max()

    println("max1: "   maxAmountForConsecutive)

CodePudding user response:

Besides the grouping method that Tenfour04 suggested in the comment, which I personally find a bit overkill for a task like this (but it is useful in other scenarios), you could also just simply count.

We can create a simple extension function that counts each step, then resets once it hits a isCar:false:

fun Collection<Journey>.calculateMaxByCarConsecutive(): Int {
    var max = 0
    var currentMileageSum = 0
    var count = 0

    forEach { current ->
        if (current.isCar) {
            //if the current journey is in a car, add its mileage to our current count
            currentMileageSum  = current.mileage
            //increase count that tells us how many journeys have been consecutive
            count  

            //if we have more than 1 journey creating the currentCount
            // and currentMileageSum is greater than max, then the currentMileageSum becomes the new max, equivalent to if(currentMileageSum > max) max = currentMileageSum
            if (count > 1) max = maxOf(currentMileageSum, max)
        } else {
            //reset the counters if this record is not by car
            count = 0
            currentMileageSum = 0
        } 
    }

    return max
}

Then we can call it like this:

val journeys = listOf(
    Journey(true, 10),
    Journey(true, 20),
    Journey(true, 40),
    Journey(false, 105),
    Journey(true, 10423423),
    Journey(false, 130),
    Journey(true, 40),
    Journey(true, 40),
    Journey(true, 10),
)

val maxJourneyByCar = journeys.calculateMaxByCarConsecutive()

println(maxJourneyByCar) //this will print 90

Not sure if this is more elegant that your solution or than using fold, but I do think it is simpler.

  • Related