Feature requests, eachSlice as in Ruby


#1

Ruby (and other languages) has a very nice feature with arrays where you can slice them in arbitrarily large slices. I have made an extension method that does it, but it would be nicer if it was properly done by professionals :slightly_smiling:

inline fun <reified T> arrayEachSlice(array: Array<T>, size: Int): Array<Array<T>?> {
    val result: Array<Array<T>?> = arrayOfNulls(array.size.div(size) + 1)
    for (i in 0..array.lastIndex step size) {
        result[i.div(size)] = if (i == array.lastIndex) {
            arrayOf(array.last())
        } else
            array.copyOfRange(i, if (i + size <= array.lastIndex + 1) i + size else array.lastIndex + 1)
    }
    return result
}

#2

Here is a method that emulates what you were trying to do which generates and array of arrays except I made mine an extension function that you would call like myArray.toSlicedArray(5):

inline fun <T> Array<T>.toSlicedArray(slice: Int)
        = Array((size + slice - 1) / slice)
            { copyOfRange(it * slice, Math.min(size, (it + 1) * slice)) }

But that isn’t exactly emulating Ruby’s each slice and isn’t the best way to go.

In Kotlin what you probably want is something that returns a Sequence<Array<T>>:

fun <T> Array<T>.sliceBy(slice:Int)
    = Sequence { object : Iterator<Array<T>>
        {
            var index = 0
            override fun hasNext() = index < size

            override fun next(): Array<T>
            {
                val start = index;
                index = Math.max(size, index + slice)

                return copyOfRange(start, index)
            }
        }
    }

#4

The first method needs a reified type parameter, otherwise it works like my method, but more correctly as it is a extension function (I realized my mistake after my post).

Your other suggestion I cannot get to return anything else but a sequence of the elements in the array it operates on. No matter the slice input.

Thanks!


#5

Probably true. I had the reified in there but deleted it before I posted it when the compiler didn’t complain.

Guess I should have tested it. Should be min, not max and I fixed it so that will work even if the array size + the slice length approaches Integer.MAX_VALUE. Try this version:

fun <T> Array<T>.sliceBy(slice:Int, step:Int = slice)
    = Sequence { object : Iterator<Array<T>>
        {
            var index = 0
            override fun hasNext() = index < size

            override fun next(): Array<T>
            {
                val start = index;
                index += Math.min(slice, size - index)

                return copyOfRange(start, index)
            }
        }
    }