How to build a Lens to "filter and modify" elements of list element? Using Arrow Optics

150 Views Asked by At

Using kotlin lib Arrow/optics

Is there a way to build a lens to filter and set/modify a element of list property?

like this code -

@optics
data class Order(val code: String, val lines: List<Line>){
    companion object
}

@optics
data class Line(val code: String, val number: Int){
    companion object
}


class LensTest : StringSpec({
    val order = Order("1", listOf(Line("1", 1), Line("2", 2)))

    "simple add" {
        Order.lines.modify(order) { it + Line("3", 3) } shouldBe Order(
            "1",
            listOf(Line("1", 1), Line("2", 2), Line("3", 3))
        )
    }
    "filter and set"{
        // I want to a lens which can find lines code==1, and modify its number to 100.
        // following code not work.
        // Order.lines.
        //    .filter { it.number == 1 }.set(order,100)
    }
})
1

There are 1 best solutions below

3
TranDatK On

You can achieve this using Arrow's optics library in Kotlin. You can use a combination of lenses and filters to achieve your goal. In your case, you want to find lines with a specific code and modify their number.

import arrow.optics.optics
import arrow.optics.Lens

@optics
data class Order(val code: String, val lines: List<Line>) {
    companion object
}

@optics
data class Line(val code: String, val number: Int) {
    companion object
}

fun main() {
    val order = Order("1", listOf(Line("1", 1), Line("2", 2)))

    val linesLens: Lens<Order, List<Line>> = Order.lines
    val lineLens: Lens<Line, Int> = Line.number

    val modifiedOrder = linesLens
        .filter { it.code == "1" }
        .composeLens(lineLens)
        .set(order) { 100 }

    println(modifiedOrder)
}