How to slice vararg argument

How to slice vararg argument

Problem Description:

I wrote an extension function to get an element of an JSON object by its name:

fun JSONObject.obj (name: String): JSONObject? =
    try { this.getJSONObject(name) }
    catch (e: JSONException) { null }

Now I want to extend this for nested JSON objects. I wrote the following:

tailrec fun JSONObject.obj (first: String, vararg rest: String): JSONObject? =
    if (rest.size == 0)
        obj(first)
    else
        obj(first)?.obj(rest[0], *rest.drop(1).toTypedArray())

But this looks quite inefficient to me.

What is the best way to slice a vararg argument?

Solution – 1

Instead of slicing, why don’t you try just iterating over all the objects and getting the JSONObjects? I think this would be much more efficient.

fun JSONObject.obj(vararg names: String): JSONObject? {
    var jsonObject = this
    for (name in names) {
        if (!jsonObject.has(name))
            return null
        jsonObject = jsonObject.getJSONObject(name)
    }
    return jsonObject
}

Solution – 2

We could use vararg only in the public function, but then internally use list for recursion:

fun JSONObject.obj (first: String, vararg rest: String): JSONObject? = obj(first, rest.asList())

private tailrec fun JSONObject.obj (first: String, rest: List<String>): JSONObject? =
    if (rest.size == 0)
        obj(first)
    else
        obj(first)?.obj(rest[0], rest.subList(1, rest.size))

Both asList() and subList() don’t copy data, but only wrap the existing collection. Still, this is far from ideal, because it creates a new object for each iteration and it may create a chain of views (it depends on internal implementation of subList()). Alternatively, the internal function could receive an array and offset – this will solve both above problems.

Generally, I suggest to not try turning Kotlin into something it is not. It has limited support for functional constructs, but it is not a functional language. Without the linked list implementation which could be easily split into head and tail, this style of code will be always inefficient and/or cumbersome. You can look for such implementation, for example in Arrow or kotlinx.collections.immutable. The latter has ImmutableList with optimized subList() – you can use it with the solution provided above to avoid creating a chain of lists.

Update

As a matter of fact, basic lists implementations in the Java stdlib also provide optimized subList(): AbstractList.java. Therefore, the above solution using simply asList() should be fine, at least when targeting JVM.

Rate this post
We use cookies in order to give you the best possible experience on our website. By continuing to use this site, you agree to our use of cookies.
Accept
Reject