DEV Community

Discussion on: Daily Challenge #118 - Reversing a Process

Collapse
 
jbristow profile image
Jon Bristow • Edited

An overengineered arrow-kt solution where I never check explicitly for null.

Probably uglier than needed, but fun.

import arrow.core.Option
import arrow.core.getOption
import arrow.core.getOrElse
import arrow.core.toOption

const val errorMessage = "Impossible to decode"
const val asciiValOfA = 'a'.toInt()

const val alphabet = "abcdefghijklmnopqrstuvwxyz"

fun Char.decode(n: Int) = (((toInt() - asciiValOfA) * n) % 26 + asciiValOfA).toChar()

fun MatchResult.stringValueGroup(n: Int) = groups[n].toOption().map { it.value }
fun MatchResult.intValueGroup(n: Int) = groups[n].toOption().map { it.value.toInt() }

fun String.decode() =
    """(\d+)([a-z]+)""".toRegex()
        .find(this).toOption()
        .flatMap { mr ->
            mr.stringValueGroup(2).flatMap { s ->
                mr.intValueGroup(1).flatMap {
                    generateAlphaMap(it).flatMap { rmap ->
                        s.fold(Option.just(""), convertingStringFolder(it, rmap))
                    }
                }
            }
        }.getOrElse { errorMessage }

private fun generateAlphaMap(num: Int) =
    alphabet.map { c -> c.decode(num) to c }.toMap().let { alphamap ->
        when (alphamap.size) {
            26 -> Option.just(alphamap.map { (k, v) -> v to k }.toMap())
            else -> Option.empty()
        }
    }

private fun convertingStringFolder(num: Int, rmap: Map<Char, Char>) = { acc: Option<String>, inputChar: Char ->
    acc.flatMap { soFar ->
        rmap.getOption(inputChar).map { c -> "$soFar${c.decode(num)}" }
    }
}

fun main() {
    println("6015ekx".decode())
    println("5057aan".decode())
}