Data class get all components/properties

Hello, I suppose, this question was raised but how can I get all properties of a data class? It is possible to get a componentN but what’s the purpose if I can’t get them all in a loop and even don’t know the N? I need to make .forEach for all the properties and currently to do it better than use reflection, I rewrote the whole class to a hash map.

If you put the code here maybe help on help.
Just the data class part and what you can do with.

The componentN functions are there to support destructuring. If you want to get all of the properties of some random data class, that’s what reflection is for.

Question: why doesn’t your code know what data class it’s working with?

data class some (val a: Int =  0, val b: Int =  2, val c:Int =  3){
    fun getAll() = this.components.forEach{ something }
}

The data class has above 30 parameters, other way I need to repeat their names every time.

The problem is that each property of a data class could have a different type.

If implemented in a generic fashion, the loop action would only be able to consume Any? values, and therefore it would be something like this :

    fun forEachProperty(action: (Pair<String, Any?>) -> Unit);

That looks hardly usable / efficient for me.

Now, if you’re in the particular case where all your properties have the same type, I don’t think that any convenient solution exists yet.

You could create a compiler plugin or something similar that creates a forEach extension function matching your case.

I see another workaround that might provide you with the forEach. But in the same time, it will cause you to lose almost all other useful tools of dataclass (copy method, equality method, etc.) : you can create a class decorating a list or an array, and then associate a property to each index :

class IntValueForm(private val values: IntArray) {
    companion object { val PROPERTY_COUNT : Int = 3 }
    init {
        require(values.size == PROPERTY_COUNT)
    }

    val a : Int get() = values[0]
    val b: Int get() = values[1]
    val c: Int get() = values[2]

    fun forEach(action: (Int) -> Unit) = values.forEach(action)
}

So, if your class has 100+ properties, you’ll rewrite them all? Sorry, but it’s not usable in modern world. And there can be a lot of different data classes with different amount of properties.

One may argue that having 100+ properties in a single class is not usable at all, no matter the language.

Could you write something more about your specific use case? Why not just use a collection in the first place, if you need to iterate over the values?

5 Likes

I cannot for the life of me imagine a use case for a data class with 100+ properties. It sounds like having a database with only one table but hundreds of columns… I.e. something in the design of your data structures has gone seriously wrong.

Depending on your use case, you should either pick that thing apart into multiple classes so you can actually keep separate data separate and understandable, or you are trying to use a data class when you should be using a map (if all properties in your data class have the same type, it’s almost definitely the later).

3 Likes

You can try to gen any external JSON response, like movie info or other stuff. There are a lot of parameters to serialize, e.g.

Did you even read the previous comment?
How would you loop through the properties? These properties are likely of different types, and looping via index is done at runtime, the best you can get is looping through the properties and consume them as Any?, which, as mentioned, isn’t very usable. You will have to either use reflection if you really need to loop through the fields.

Ok, currently I have about 30+ parameters same type for a single data class. And I need to proceed different data classes. Think to use map for them.

If you have 30+ parameters with the same type, you probably should use an IntArray or a List?

I don’t find a proper way to store parameters names and other data in a List better than in a map.

Wow in that case don’t use a data class at all. Just use a generic JSON type object. That way you at least know you will get a more limited number of basic JSON types (maps, arrays, ints, strings etc.). Many JSON libraries provide that for you Not sure how genetically looping that would be useful but it would accomplish what you need without having to account for some crazy number of types. If you still need data classes for more explicit field access you could still do that but that wouldn’t be your choice for looping. Two separate flows there depending on need.

1 Like

reflection is all you need

Hash map may be indeed the better solution.

1 Like

yes, but its parameters are unstable/unknown.

I agree with others that it seems you’re trying to do something strange. You should almost never need a class with so many properties. But if you really want to do this then Kotlin provides an utility that fits nicely between a map and an object with properties. See this example:

fun main() {
    val foo = MyClass()
    foo.prop1 = "value1"
    foo.prop2 = "value2"
    foo.prop3 = "value3"
    
    // {prop1=value1, prop2=value2, prop3=value3}
    println(foo.properties)
}

class MyClass private constructor(val properties: MutableMap<String, String>) {
    constructor() : this(mutableMapOf())

    var prop1 by properties
    var prop2 by properties
    var prop3 by properties
}

It has worse performance than regular properties, because it stores props in a map. But it lets you easily list all properties, iterate over them, etc.

Yeah, but looks better to add components.all to the data class.