DEV Community

Discussion on: Daily Challenge #121 - Who has the most money?

Collapse
 
jbristow profile image
Jon Bristow

Ok, I fixed it. It's not too much uglier with proper Option protection...

import arrow.core.Option
import arrow.core.getOrElse

data class Student(val name: String, val fives: Int, val tens: Int, val twenties: Int)

private val Student.total: Int
    get() = fives * 5 + tens * 10 + twenties * 20

sealed class StudentAccumulator {
    abstract val money: Option<Int>
}

object Empty : StudentAccumulator() {
    override val money = Option.empty<Int>()
}

data class ErrorFound(val message: String) : StudentAccumulator() {
    override val money = Option.empty<Int>()
}

data class MaxFound(val name: String, override val money: Option<Int>) : StudentAccumulator() {
    constructor(student: Student) : this(student.name, Option.just(student.total))
}

data class TieFound(override val money: Option<Int>) : StudentAccumulator() {
    constructor(student: Student) : this(Option.just(student.total))
}

fun generateStudentFolder(compareFn: (StudentAccumulator, Student) -> Option<Int>) =
    { acc: StudentAccumulator, student: Student ->
        when (acc) {
            is ErrorFound -> acc
            is Empty -> MaxFound(student)
            is MaxFound -> acc.consider(student, compareFn)
            is TieFound -> acc.consider(student, compareFn)
        }
    }

fun MaxFound.consider(student: Student, compareFn: (StudentAccumulator, Student) -> Option<Int>) =
    compareFn(this, student).map { result ->
        when (result) {
            -1 -> MaxFound(student)
            0 -> TieFound(student)
            else -> this
        }
    }.getOrElse { ErrorFound("Bad comparison.") }

fun TieFound.consider(student: Student, compareFn: (StudentAccumulator, Student) -> Option<Int>) =
    compareFn(this, student).map { result ->
        when (result) {
            -1 -> MaxFound(student)
            0 -> ErrorFound("More than one maximum")
            else -> this
        }
    }.getOrElse { ErrorFound("Bad comparison.") }

fun Iterable<Student>.findMax() =
    fold(
        Empty as StudentAccumulator,
        generateStudentFolder { a, b ->
            a.money.map { it.compareTo(b.total) }

        }
    )

fun Iterable<Student>.findMaxName(): String {
    return when (val result = findMax()) {
        is ErrorFound -> throw Error(result.message)
        is Empty -> throw Error("No max found")
        is MaxFound -> result.name
        is TieFound -> "all"
    }
}