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)
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 currentMileageSum
//and
//currentMileageSum is greater than max,
// then the currentMileageSum becomes the new max
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.