Transform Map Values to Data Class in Kotlin - kotlin

I have a map whose values are an arraylist of different object types (LocalDate, LocalTime, Float, List(Float)).
I would like to transform the map values to a custom data class i have created.
I have tried destructuring the maps value components however i am getting error "Destructuring declaration initializer of type Map.Entry<String, Any> must have a 'component3()' function".
How would i perform this transformation below?
Blockquote
val localDate1 = LocalDate.now()
val localTime1 = LocalTime.now()
val float1 = 1f
val floatList1 = listOf<Float>(1f, 2f, 3f)
val localDate2 = LocalDate.now()
val localTime2 = LocalTime.now()
val float2 = 2f
val floatList2 = listOf<Float>(4f, 5f, 6f)
val myMap = HashMap<String, Any>()
myMap["keyOne"] = arrayListOf<Any>(localDate1, localTime1, float1, floatList1)
myMap["keyTwo"] = arrayListOf<Any>(localDate2, localTime2, float2, floatList2)
val newMap = myMap.mapValues { (date, time, float, floatList) -> /*Here i am getting destructuring error*/
CustomObject(
date,
time,
float,
floatList
)
}
Blockquote
data class CustomObject (
val date: LocalDate,
val time: LocalTime,
val float: Float,
val floatList: List<Float>
)

Map.mapValues() takes a lambda whose receiver is the map Entry, not its value.  (The method is named for the lambda's results, not its parameter.) So you can't destructure it that way.
There are also typecasting issues: myMap is a map whose values are Any, but you seem to be assuming that each value is actually a List of Any (with the actual elements being of the appropriate types in the appropriate order). So you need to do the necessary casting to tell the compiler what types they are (or at least, what types you assume they are…)
So you could destructure it as a separate step, e.g.:
val newMap = myMap.mapValues { entry ->
val (date, time, float, floatList) = entry.value as List<Any>
CustomObject(
date as LocalDate,
time as LocalTime,
float as Float,
floatList as List<Float>
)
}
Or you could just use the array values directly:
val newMap = (myMap as Map<String, List<Any>>).mapValues {
CustomObject(
it.value[0] as LocalDate,
it.value[1] as LocalTime,
it.value[2] as Float,
it.value[3] as List<Float>
)
}
However I think it would be a lot better to avoid having the lists in the first place. You're taking a lot of extra code to create the lists and then convert them — but much of that code is unsafe: each destructuring can fail with an exception (perhaps ArrayIndexOutOfBoundsException) if the list isn't long enough, and each cast can fail with a ClassCastException. Casts are always a code smell, especially unchecked ones like these; Kotlin's type system is powerful enough to express most things you're likely to want, so bypassing it like this tends to indicate that the design can be improved.
So if there's any way you can create your custom objects directly, that's likely to be shorter, safer, faster, and easier to read.

import java.time.LocalDate
import java.time.LocalTime
data class CustomObject(
val date: LocalDate,
val time: LocalTime,
val float: Float,
val floatList: List<Float>
)
val myMap = mapOf(
"keyOne" to listOf(LocalDate.now(), LocalTime.now(), 1f, listOf(1f, 2f, 3f)),
"keyTwo" to listOf(LocalDate.now(), LocalTime.now(), 2f, listOf(4f, 5f, 6f))
)
val newMap = myMap.mapValues {
CustomObject(
it.value[0] as LocalDate,
it.value[1] as LocalTime,
it.value[2] as Float,
(it.value[3] as List<*>).map { fl -> fl as Float }
)
}
println(newMap)

Related

What is the type of mutablelistof(2, 'j') in kotlin

Fun main(){
Val a = mutableListOf(2, 'j')
}
// What is the type of a?
i want to make it like this: val a = mutableListOf , but i dont the type
Common supertypes of Int and Char are Comparable<*> and Serializable, so the type of a is:
MutableList<Comparable<*> & Serializable>
Depending on what you will do with the resulting list (e.g. initialize a property, return from a function, etc.), some of the type information will be lost and the type will be "simplified" into:
MutableList<out Any>
or:
MutableList<Any>

Jackson parses a map of strings to a declared class as a map of strings to maps. How do I make it create objects of the declared class instead?

I'm using jackson-module-kotlin:2.11.2 to parse some YAML. I'm trying to produce objects which contain a map, whose values are objects of a class that I have declared. This map instead contains values which are HashMaps.
Here are my declarations:
import com.fasterxml.jackson.module.kotlin.readValue
object Parser {
// ObjectMapper is thread safe as long as we don't mess with the config after this declaration.
val mapper: ObjectMapper = ObjectMapper(YAMLFactory()).registerKotlinModule()
.registerModule(JavaTimeModule())
.registerModule(nullMapDeserialiserModule)
.registerModule(SimpleModule().setDeserializerModifier(ValidatingDeserializerModifier()))
// When parsing timestamps, we don't want to lose the offset information
.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
// We would prefer an error if we're trying to store a float in an int
.disable(DeserializationFeature.ACCEPT_FLOAT_AS_INT)
// If a primitive field (like Int) is non-nullable (as in the Kotlin meaning), then we don't want nulls being converted to 0
.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
// Because enums could change order in future versions (if we keep them in lexicographic order, for example),
// we don't want the client to expect that they can give us the ordinal value of the enum.
.enable(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)
// When serialising schedule expressions, don't include null values
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
#Throws(JsonProcessingException::class)
inline fun <reified T: Any> deserialise(yaml: String): T = mapper.readValue(yaml)
}
data class ListValue (
val listValueKey: String,
val someOtherValue: Int
)
data class ExpectedValue (
val expectedValueKey: String,
val list: List<ListValue>
)
data class TestClass (
val testClassKey: String,
#param:JsonDeserialize(contentAs = ExpectedValue::class)
val testMap: Map<String, ExpectedValue>
)
Here is my test case:
#Test
fun `map parse test`() {
val testObj: TestClass? = RuleParser.deserialise(
//language=YAML
"""
testClassKey: the-key
testMap:
key1:
expectedValueKey: subKey1
list:
- listValueKey: someKey1
someOtherValue: 5
- listValueKey: anotherKey1
someOtherValue: 6
key2:
expectedValueKey: subKey2
list:
- listValueKey: someKey2
someOtherValue: 7
- listValueKey: anotherKey2
someOtherValue: 8
"""
)
assertTrue(testObj is TestClass)
assert(testObj.testMap is HashMap)
assertNotNull(testObj.testMap["key1"])
assert(testObj.testMap["key1"] is ExpectedValue)
assertEquals(
ExpectedValue(
expectedValueKey = "subKey1",
list = listOf(ListValue("someKey1", 5), ListValue("anotherKey1", 6))
),
testObj.testMap["key1"]
)
}
Currently, this test is failing on the final assertion, with the following error
Expected :ExpectedValue(expectedValueKey=subKey1, list=[ListValue(listValueKey=someKey1, someOtherValue=5), ListValue(listValueKey=anotherKey1, someOtherValue=6)])
Actual :{expectedValueKey=subKey1, list=[{listValueKey=someKey1, someOtherValue=5}, {listValueKey=anotherKey1, someOtherValue=6}]}
This is clearly not what I expected. If I instead parse a list of a declared class, this works correctly (example test follows).
#Test
fun `list parse test`() {
val testObj: ExpectedValue? = RuleParser.deserialise(
//language=YAML
"""
expectedValueKey: subKey1
list:
- listValueKey: someKey1
someOtherValue: 5
- listValueKey: anotherKey1
someOtherValue: 6
"""
)
assertTrue(testObj is ExpectedValue)
assertTrue(testObj.list[0] is ListValue)
assertEquals(
ListValue("someKey1", 5),
testObj.list[0]
)
}
So I'm a bit surprised that it is possible to parse a generic list in this way, but not a map. How do I get Jackson to create the map values that I expect, rather than HashMaps?
Your deserializer function is wrong. You must use the reified generic type in readValue method:
inline fun <reified T: Any> deserialise(yaml: String): T = mapper.readValue(yaml, T::class.java)
While it is odd that the annotation is needed at all (since it is not needed for lists of a declared type), it does work if the annotation is used as follows:
#field:JsonDeserialize(`as` = HashMap::class, contentAs = ExpectedValue::class)
This was not clear at first, because that javadoc for contentAs does not mention that as is also required.

How to partition and typecast a List in Kotlin

In Kotlin I can:
val (specificMembers, regularMembers) = members.partition {it is SpecificMember}
However to my knowledge I can not do something like:
val (specificMembers as List<SpecificMember>, regularMembers) = members.partition {it is SpecificMember}
My question would be - is there's an idiomatic way to partition iterable by class and typecast it those partitioned parts if needed.
If you require that functionality more often, you may just reimplement the actual partition according to your needs, e.g.:
inline fun <reified U : T, T> Iterable<T>.partitionByType(): Pair<List<U>, List<T>> {
val first = ArrayList<U>()
val second = ArrayList<T>()
for (element in this) {
if (element is U) first.add(element)
else second.add(element)
}
return Pair(first, second)
}
with a usage similar as to follows:
val (specificMembers, regularMembers) = members.partitionByType<SpecificMember, Member>()
// where specificMembers : List<SpecificMember>
// and regularMembers : List<Member> for this example
Note that this way you can also set the second type to a more generic one. I leave that up to you whether this makes sense. At least this way an unchecked cast isn't necessary.
The alternative is also shown by Simon with the let-usage. You can also directly cast the result of partition (without let and another Pair) to whatever fits, e.g.:
val (specificMembers, regularMembers) = members.partition {it is SpecificMember} as Pair<List<SpecificMember>, List<Member>>
The partition function will return a Pair<List<T>, List<T>> with T being the generic type of your Iterable. You can transform the partitioned values again using e.g. let:
val (specificMembers, regularMembers) = lists
.partition { it is SpecificMember }
.let { Pair(it.first as List<SpecificMember>, it.second) }

Custom 'typesafe' Int Types

What I would like to have is two different integer types which are semantically distinguishable.
E.g. in this code a 'Meter' type and a 'Pixel' int type
typealias Meter = Int
typealias Pixel = Int
fun Meter.toPixel() = this * 100
fun Pixel.toMeter() = this / 100
fun calcSquareMeters(width: Meter, height: Meter) = width * height
fun calcSquarePixels(width: Pixel, height: Pixel) = width * height
fun main(args: Array<String>) {
val pixelWidth: Pixel = 50
val pixelHeight: Pixel = 50
val meterWidth: Meter = 50
val meterHeight: Meter = 50
calcSquareMeters(pixelWidth, pixelHeight) // (a) this should not work
pixelWidth.toPixel() // (b) this should not work
}
The problem with this solution is
(a) that I can call calcSquareMeters with my 'Pixel' type which I don't want to be possible and
(b) that I can call the toPixel() extension function which I only want to have for my 'Meter' type on my 'Pixel' type which I don't want to be possible.
I guess this is the intended behaviour of typealias, so I guess to achieve my goal I have to use something different than typealias...
So how can I achieve this?
In addition to the existing answer: If you have a lot of common functionality between the two types and don't want to duplicate it, you can work with an interface:
interface MetricType<T> {
val value: Int
fun new(value: Int): T
}
data class Meter(override val value: Int) : MetricType<Meter> {
override fun new(value: Int) = Meter(value)
}
data class Pixel(override val value: Int) : MetricType<Pixel> {
override fun new(value: Int) = Pixel(value)
}
Like this, you can easily define operations on the base interface, such as addition, subtraction and scaling:
operator fun <T : MetricType<T>> T.plus(rhs: T) = new(this.value + rhs.value)
operator fun <T : MetricType<T>> T.minus(rhs: T) = new(this.value + rhs.value)
operator fun <T : MetricType<T>> T.times(rhs: Int) = new(this.value * rhs)
The combination of interface and generics ensures type safety, so you do not accidentally mix types:
fun test() {
val m = Meter(3)
val p = Pixel(7)
val mm = m + m // OK
val pp = p + p // OK
val mp = m + p // does not compile
}
Keep in mind that this solution comes at a runtime cost due to the virtual functions (compared to rewriting the operators for each type separately). This in addition to the overhead of object creation.
Indeed, typealiases don't guarantee this sort of type safety. You'll have to create wrapper classes around an Int value instead to achieve this - it's a good idea to make these data classes so that equality comparisons work on them:
data class Meter(val value: Int)
data class Pixel(val value: Int)
Creation of instances of these classes can be solved with extension properties:
val Int.px
get() = Pixel(this)
val pixelWidth: Pixel = 50.px
The only problematic thing is that you can no longer directly perform arithmetic operations on Pixel and Meter instances, for example, the conversion functions would now look like this:
fun Meter.toPixel() = this.value * 100
Or the square calculations like this:
fun calcSquareMeters(width: Meter, height: Meter) = width.value * height.value
If you really need direct operator use, you can still define those, but it will be quite tedious:
class Meter(val value: Int) {
operator fun times(that: Meter) = this.value * that.value
}
fun calcSquareMeters(width: Meter, height: Meter) = width * height
There is a proposal (not yet guaranteed to be accepted) to add inline classes for this purpose. I.e.
#InlineOnly inline class Meter(val value: Int)
will really be an Int at runtime.
See https://github.com/zarechenskiy/KEEP/blob/28f7fdbe9ca22db5cfc0faeb8c2647949c9fd61b/proposals/inline-classes.md and https://github.com/Kotlin/KEEP/issues/104.
From kotlin doc:
Type aliases do not introduce new types. They are equivalent to the corresponding underlying types. When you add typealias Predicate and use Predicate in your code, the Kotlin compiler always expand it to (Int) -> Boolean. Thus you can pass a variable of your type whenever a general function type is required and vice versa
This means that there isn't possible check over your typealias, and you are rally declaring your extensions functions as:
fun Int.toPixel() = this * 100
fun Int.toMeter() = this / 100
fun calcSquareMeters(width: Int, height: Int) = width * height
fun calcSquarePixels(width: Int, height: Int) = width * height
I fear the only way to achieve that you want is implementing an extra class for each type.
I would also go with the solution from TheOperator. But I would like to add the inline keyword to the operator functions. By doing so you could avoid a virtual function call when ever you use this operators.
inline operator fun <T : MetricType<T>> T.plus(rhs: T) = new(this.value + rhs.value)
inline operator fun <T : MetricType<T>> T.minus(rhs: T) = new(this.value + rhs.value)
inline operator fun <T : MetricType<T>> T.times(rhs: Int) = new(this.value * rhs)

Function definition: fun vs val

I'm curious about what is the suggested way to define member functions in Kotlin. Consider these two member functions:
class A {
fun f(x: Int) = 42
val g = fun(x: Int) = 42
}
These appear to accomplish the same thing, but I found subtle differences.
The val based definition, for instance, seems to be more flexible in some scenarios. That is, I could not work out a straight forward way to compose f with other functions, but I could with g. To toy around with these definitions, I used the funKTionale library. I found that this does not compile:
val z = g andThen A::f // f is a member function
But if f were defined as a val pointing to the same function, it would compile just fine. To figure out what was going on I asked IntelliJ to explicitly define the type of ::f and g for me, and it gives me this:
val fref: KFunction1<Int, Int> = ::f
val gref: (Int) -> Int = g
So one is of type KFunction1<Int, Int>, the other is of type (Int) -> Int. It's easy to see that both represent functions of type Int -> Int.
What is the difference between these two types, and in which cases does it matter? I noticed that for top-level functions, I can compose them fine using either definition, but in order to make the aforementioned composition compile, I had to write it like so:
val z = g andThen A::f.partially1(this)
i.e. I had to partially apply it to this first.
Since I don't have to go through this hassle when using vals for functions, is there a reason why I should ever define non-Unit member functions using fun? Is there a difference in performance or semantics that I am missing?
Kotlin is all about Java interoperability and defining a function as a val will produce a completely different result in terms of the interoperability. The following Kotlin class:
class A {
fun f(x: Int) = 42
val g = fun(x: Int) = 42
}
is effectively equivalent to:
public class A {
private final Function1<Integer, Integer> gref = new Function1<Integer, Integer>() {
#Override
public Integer invoke(final Integer integer) {
return 42;
}
};
public int f(final int value) {
return 42;
}
public Function1<Integer, Integer> getG() {
return gref;
}
}
As you can see, the main differences are:
fun f is just a usual method, while val g in fact is a higher-order function that returns another function
val g involves creation of a new class which isn't good if you are targeting Android
val g requires unnecessary boxing and unboxing
val g cannot be easily invoked from java: A().g(42) in Kotlin vs new A().getG().invoke(42) in Java
UPDATE:
Regarding the A::f syntax. The compiler will generate an extra Function2<A, Integer, Integer> class for every A::f occurrence, so the following code results in two extra classes with 7 methods each:
val first = A::f
val second = A::f
Kotlin compiler isn't smart enough at the moment to optimize such kind of things. You can vote for the issue here https://youtrack.jetbrains.com/issue/KT-9831. In case you are interested, here is how each class looks in the bytecode: https://gist.github.com/nsk-mironov/fc13f2075bfa05d8a3c3
Here's some code showing how f and g are different when it comes to usage:
fun main(args: Array<String>) {
val a = A()
exe(a.g) // OK
//exe(a.f) // does not compile
exe { a.f(it) } // OK
}
fun exe(p: (Int) -> Int) {
println(p(0))
}
Where f and g are:
fun f(x: Int) = 42
val g = fun(x: Int) = 42
You can see that g is an object that can be used like a lambda, but f cannot. To use f similarly, you have to wrap it in a lambda.