split a ByteArray to multiple ByteArrays by a single byte

131 Views Asked by At

I'm new to Kotlin development and I'm trying to use kotlin standard functions like split,partition,chunked,windowed to solve this problem and understand them better

I have a ByteArray like this

[123, 34, 97, 3, 116, 3, 111, 110, 34, 58, 3, 112, 105, 110]

i need to split by 3 so the result should be

[123, 34, 94]
[116]
[111, 110, 34, 58]
[112, 105, 110]
2

There are 2 best solutions below

3
lukas.j On
val list = listOf(123, 34, 97, 3, 116, 3, 111, 110, 34, 58, 3, 112, 105, 110)

val result = list.fold(mutableListOf<MutableList<Int>>()) { acc, value ->
  when {
    value == 3    -> acc.add(mutableListOf())
    acc.isEmpty() -> acc.add(mutableListOf(value))
    else          -> acc.last().add(value)
  }
  acc
}

println(result)

Output:

[[123, 34, 97], [116], [111, 110, 34, 58], [112, 105, 110]]

Edit: added the following version with an array containing 1 million unsigned bytes.

import kotlin.random.Random

val arrayOfUBytes = List(1_000_000) { Random.nextInt(0, 255).toUByte() }.toTypedArray()

val result = arrayOfUBytes
  .fold(mutableListOf<MutableList<UByte>>()) { acc, value ->
    when {
      value == 3.toUByte() -> acc.add(mutableListOf())
      acc.isEmpty()        -> acc.add(mutableListOf(value))
      else                 -> acc.last().add(value)
    }
    acc
  }

println(result)
0
dnault On

I'm ready to be surprised (and often am!), but I don't think any built-in method does exactly what you want.

If efficiency is the primary concern, you could iterate over the array looking for the delimiter, and then slice the array at those indexes.

This way each chunk is a ByteArray instead of a List<Byte>, and no boxing is required behind the scenes.

fun ByteArray.split(delimiter: Byte): List<ByteArray> {
    val result = mutableListOf<ByteArray>()
    var prevStart = 0
    for (i in this.indices) {
        if (this[i] == delimiter) {
            result.add(this.sliceArray(prevStart until i))
            prevStart = i + 1
        }
    }
    result.add(this.sliceArray(prevStart..this.lastIndex))
    return result;
}

fun main() {
    byteArrayOf(123, 34, 97, 3, 116, 3, 111, 110, 34, 58, 3, 112, 105, 110)
        .split(3)
        .forEach { println(it.contentToString()) }
}

Output:

[123, 34, 97]
[116]
[111, 110, 34, 58]
[112, 105, 110]