Home > Blockchain >  fold/reduce with complex accumulator
fold/reduce with complex accumulator

Time:03-25

I have a list that looks like this:

val myList = listOf(
    Message(
      id= 1,
      info = listOf(1, 2)
    ),
    Message(
      id= 1,
      info = listOf(3, 4)
    ),
    Message(
      id= 2,
      info = listOf(5, 6)
    ) 
)

How can I convert it so the elements with the same id are combined?

listOf(
    Message
      id= 1
      info = listOf(1, 2, 3, 4)
    ),
    Message
      id= 2
      info = listOf(5, 6)
    ) 
)

I've tried the following, and it works


myList
    .groupBy { it.id }   
    .map { entry ->
        val infos = entry.value.fold(listOf<Int>()) { acc, e -> acc   e.info }

        Message(
            id = entry.key,
            info = infos
        )
    }

But I was wondering if there was an easier/cleaner/more idiomatic way to merge these objects. It seems like I would be able to do this with a single fold, but I can't wrap my brain around it.

Thanks

CodePudding user response:

You can groupingBy the ids, then reduce, which would perform a reduction on each of the groups.

myList.groupingBy { it.id }.reduce { id, acc, msg -> 
    Message(id, acc.info   msg.info) 
}

This will of course create lots of Message and List objects, but that's the way it is, since both are immutable. But there is also a chance that this doesn't matter in the grand scheme of things.

If you had a MutableMessage like this:

data class MutableMessage(
    val id: Int,
    val info: MutableList<Int>
)

You could do:

myList.groupingBy { it.id }.reduce { _, acc, msg ->
    acc.also { it.info.addAll(msg.info) }
}

CodePudding user response:

A solution without using reduce or fold:

data class Message(val id: Int, val info: List<Int>)

val list = listOf(
  Message(id = 1, info = listOf(1, 2)),
  Message(id = 1, info = listOf(3, 4)),
  Message(id = 2, info = listOf(5, 6))
)

val result = list
  .groupBy { message -> message.id }
  .map { (_, message) -> message.first().copy(info = message.map { it.info }.flatten() ) }

result.forEach(::println)

CodePudding user response:

By extracting out a few functions which have a meaning of their own, You can make it readable to a great extent.

data class Message(val id: Int, val info: List<Int>) {
    fun merge(that: Message): Message = this.copy(info = this.info   that.info)
}

fun List<Message>.mergeAll() =
    this.reduce { first, second -> first.merge(second) }

fun main() {
    val myList = listOf(
        Message(
            id = 1,
            info = listOf(1, 2)
        ),
        Message(
            id = 1,
            info = listOf(3, 4)
        ),
        Message(
            id = 2,
            info = listOf(5, 6)
        )
    )

    val output = myList
        .groupBy { it.id }
        .values
        .map { it.mergeAll() }

    println(output)
}
  • Related