'Kotlin: Iterate over components of object

Each data class object has a component for each property like component1, component2, etc.. I was wondering if there is any way in Kotlin to iterate over each component of a class. Say I have class:

data class User(age:Int, name:String)

Could I do something like:

for(component in aUserObject){
    //do some stuff with age or name
}

?



Solution 1:[1]

First of all, the componentN properties are available only on data classes, not on every object.

There is no API specifically for iterating over the components, but you can use the Kotlin reflection to iterate over properties of any class:

class User(val age: Int, val name: String)

fun main(args: Array<String>) {
    val user = User(25, "Bob")
    for (prop in User::class.memberProperties) {
        println("${prop.name} = ${prop.get(user)}")
    }  
}

Solution 2:[2]

also, you can use Properties Delegation with built-in delegate 'by map'. it's very useful for some simple stat classes.

class DbUsingStat {
  private val map = mutableMapOf(
    "removed" to 0,
    "updated" to 0,
    "skipped" to 0
  )

  var removed by map
  var updated by map
  var skipped by map

  fun asMap() : Map<String, Int> = map.toMap()
}

...
...


val someStatistic = DbUsingStat().apply {
  skipped = 5
  removed = 10
  updated = 1505
}

for((k, v) in someStatistic.asMap())
  println("$k: $v")

Solution 3:[3]

For anyone looking for a reflection-free solution, here is one.

Important: This requires you to be able to modify the User data class.

You can write your own iterator function into the data class, like so:

data class User(age: Int, name: String) {
    operator fun iterator(): Iterator<Pair<String, Any>> {
        return listOf("age" to age, "name" to name)
    }
}

Even though it's a bit of work at first, this allows you to use a for-each style loop like this because Kotlin calls the iterator() function in the background to make for-each loops work:

val aUserObject: User = User(36, "John Doe")
for ((key, value) in aUserObject) {
    println("$key: $value") // age: 36
                            // name: John Doe
}

This keeps your User class a data class and doesn't require the use of Reflection.

Solution 4:[4]

In kotlin you can do this in the following way, by iterating over the object fields, inside it, is using reflection for this.

 private fun prepareData(covidResponse: CovidResponse) {
    for (field in covidResponse.javaClass.declaredFields){
              responseList.add(field as Object)
    }
}

Solution 5:[5]

here is an idea...i am not satisfied with it...but nonetheless here it is.

it has some pros and cons:

  • pros:
    • type-safe/compile-time checks
      • adding/removing fields to/from the data class causes compiler errors at field-iteration sites
    • no boiler-plate code needed
  • cons:
    • won't work if default values are defined for arguments

declaration:

data class Memento(
    val testType: TestTypeData,
    val notes: String,
    val examinationTime: MillisSinceEpoch?,
    val administeredBy: String,
    val signature: SignatureViewHolder.SignatureData,
    val signerName: String,
    val signerRole: SignerRole
) : Serializable

iterating through all fields:

val iterateThroughAllMyFields: Memento = someValue
Memento(
    testType = iterateThroughAllMyFields.testType.also { testType ->
        // do something with testType
    },
    notes = iterateThroughAllMyFields.notes.also { notes ->
        // do something with notes
    },
    examinationTime = iterateThroughAllMyFields.examinationTime.also { examinationTime ->
        // do something with examinationTime
    },
    administeredBy = iterateThroughAllMyFields.administeredBy.also { administeredBy ->
        // do something with administeredBy
    },
    signature = iterateThroughAllMyFields.signature.also { signature ->
        // do something with signature
    },
    signerName = iterateThroughAllMyFields.signerName.also { signerName ->
        // do something with signerName
    },
    signerRole = iterateThroughAllMyFields.signerRole.also { signerRole ->
        // do something with signerRole
    }
)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Yevhenii Nadtochii
Solution 3 MajorTanya
Solution 4 Sahil Bansal
Solution 5 Eric