Home > Software design >  How to slice vararg argument
How to slice vararg argument

Time:11-20

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?

CodePudding user response:

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 = this.getJSONObject(name)
    }
    return jsonObject
}

CodePudding user response:

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.

  • Related