How can I divide a list like this? Imagine my inpu...
# announcements
n
How can I divide a list like this? Imagine my input is:
111212221
I want my output to be:
[111], [2], [1], [222], [1]
d
Can you clarify the types on the input and output?
Is the input a string of integers? An integer? A list of single digit integers?
n
It's a complex type, the numbers are just an example. I'd like it to behave like
groupBy
except split into lists that are in order
n
list.fold()
n
@nil2l I saw the fold operation, but I'm not sure how I make it create multiple lists. Can you give a quick example?
d
Copy code
//val input = listOf(1, 1, 1, 2, 1, 2, 2, 2, 1)
fun firstIdea(input: List<Int>): List<List<Int>> {
    val indicesOfChange = input.asSequence().zipWithNext { a, b -> a != b }
        .mapIndexedNotNull { index, changed -> if (changed) index else null }
    val sublistIndexes = sequence {
        yield(0)
        yieldAll(indicesOfChange)
        yield(input.lastIndex)
    }
    return sublistIndexes.zipWithNext { start, endInclusive -> input.subList(start, endInclusive + 1) }.toList()
}
The optimal way to do this is with a "raw" for loop.
g
@Dominaezzz I would have never thought to see yield used in code which isn’t an example. Or my experience level is just to low. XD
d
Aha, I saw the opportunity and snatched it.
n
Kotlin way:
Copy code
"111212221".map { it.toString().toInt() }
    .fold(emptyList<List<Int>>()) { acc, i ->
        val lastGroup = acc.takeIf { it.isNotEmpty()}?.last()
        val last = lastGroup?.last()
        if (last != i) {
            // start new group
            acc + listOf(listOf(i))
        } else {
            // add to the last group
            acc.take(acc.size - 1) + listOf(lastGroup + i)
        }
    }
👍 1
n
Interesting approaches. @nil2l yours seems a lot like what I did without fold. I find that one more intuitive. Thank you both for your help!
Does anyone know if there's a common name for an operation like this?
d
chunk?
n
It may be optimized by using
Pair
as
acc
(to not recreate last every time)
Copy code
Pair< List<List<Int>>, List<Int> >
But idea is so.
1
n
Chunk seems close to me, although that's already used for simple size divisions. Maybe bucket?
n
Some chaining maybe. Calls chaining?
n
Maybe
groupInPlace
for ultimate readability
n
Oh. You mean this operation.
n
Yeah, this type of operation. I'm not sure if there's a name for it in general. I'd like to know if there was
n
nwhGroup
😯 1
😂 1
d
I quite like that name.
n
I guess it’s more like reducing, not grouping.
d
contiguousGroupBy
. It's a mouthful though.
👍 1
m
i have previously written this:
Copy code
inline fun <T> Iterable<T>.adjacentGroupBy(keySelector: (T) -> Any? = { it }): List<List<T>> =
    this.fold(listOf<MutableList<T>>()) { acc, element ->
        if (acc.isNotEmpty() && keySelector(acc.last().last()) == keySelector(element)) {
            acc.also { it.last().add(element) }
        } else {
            acc + listOf(mutableListOf(element))
        }
    }
👍 1
I think it was inspired by some function from haskell stdlib but cannot remember the name
m
although answered beautifully in code already by people here, this would be a perfect example for a regex. You want to split a string between the characters where the before character does not equal the after character. There is a perfectly fine regex for this:
Copy code
input.split("""(?<=(.))(?!\1)""".toRegex())
e
Tested only for happy path and not for edge cases.