Targeting the first and second values in a Class<Int, Int> - kotlin

In Kotlin's Pair data class it takes 2 values:
Pair<out A, out B>
and those values are targeted via first and second named properties:
Pair(first: A, second: B)
However, how are the first and properties targeted if these properties didn't exist (e.g. for other classes with two parameters OtherClass<Int, Int>? - Is there another way to target them?)
PS: Why is Pair a data class and not a regular class?

A and B are not properties, they are type parameters. The type parameters are then used to define properties.
This is the source code for Pair:
public data class Pair<out A, out B>(
public val first: A,
public val second: B
) : Serializable {
/**
* Returns string representation of the [Pair] including its [first] and [second] values.
*/
public override fun toString(): String = "($first, $second)"
}
You target the properties (first and second), not the type parameters.
Suppose for example you have Pair<Int, String>. This means A is Int, B is String.
first is a property of type A, second is a property of type B, therefore first is an Int and second is a String.
In answer to your 'PS', Pair is a data class because of the usual benefits this confers, e.g. a generated equals() method.

Pair repersents a generic pair of two values, think of it as a box with a divider in the middle. It doesn't care what you put in each side of the divider.
What are First and Second:
If you look at Robs Answer you'll see that Pair class has two public values, one is
name "first" and the other is named "second". It basically means that every box(Pair)
has two sides and it does not care what you put in either side, by default I have named
one side as "first" and the other side as "second" if you want something from left just
refer to it as first and if you want something from right side refer to it as second.
Doing Pair("Some Words", 99) simply means you want a box that one side holds a string and the other side holds an integer.
Doing myPair = Pair("value 1", 100) simply means you want a box that one side holds a string(value 1) and the other side holds an integer(100) and you have named this box myPair.
Since Kotlin does not care about the type of things that your putting in the box, than it is perfectly okay to add a null value to a pair, like myPair=Pair(null, 99), its your job to check if there is a null value being added or extracted from the pair.
Retrieving values:
Say you do something like var myPair = Pair("value 1", 100)
If you want to see what you are holding in myPair you can do:
println(myPair.first) -> "value 1"
println(myPair.second) -> 100
Why is Pair a data class and not a regular class?
The main purpose of a data class is to hold data.
Data classes have some restrictions so the compiler can add some standard functionality to all data classes, like eaquls(), hashCode()
Pair class is meant to hold a pair of data, therefore in can benefit from some standard functionality, like toString().
You can read the details HERE

If you take a look at how Pair is implemented, you see that it's a simple data class with two properties: first and second.
public data class Pair<out A, out B>(
public val first: A,
public val second: B
)
As a result, you can obviously access these properties with this syntax: pair.first and pair.second. The names first and second are no special keywords but simply property names.
Note that there's something special about pairs and data classes in general though, which can be observed here:
val p = Pair(1, 2)
val (f: Int, s: Int) = p
println(f) //1
println(s) //2
This technique is called destructuring and is made possible through componentX operators which are automatically generated for you if you use data classes.

Related

How to group objects in a list by two fields?

I would like to group a list of objects basing on two fields in those objects.
Let's say I have an object like this:
data class ItemDataDTO(
val itemId: BigInteger?,
val sequence: BigInteger?,
val serialNumber: String?,
val pickingId: String?,
val runId: String? = null,
val warehouse: String?,
)
Now I have a list of ItemDataDTO containing a lot of items. I would like to group them by runId and pickingId (because I need those items that have the same pickingId and runId grouped somehow.)
val items: List<ItemDataDTO> = someItemRepository.getItemsForWarehouse("someWarehouseId")
val groupedItems = items.groupBy({ it.runId }, { it.pickingId })
This doesn't work. I found out that I could use groupingBy() function along with a Triple, but I want just them to be grouped by two values...
val groupedItems = items.groupingBy { Triple(it.runId, it.pickingId, null) }
But this doesn't work as well. I tried to add a third parameter instead of null, using it.warehouse:
val groupedItems = items.groupingBy { Triple(it.runId, it.pickingId, it.warehouse) }
It returns an instance of Grouping<ItemDataDTO, Triple<String?, String?, String?>> and I'm not sure what to do with this object.
What could I do to properly group those objects?
In a perfect world, I would like to transform this list to a list of something like:
data class PickingList(
val runId: String,
val pickingId: String,
val items: List<ItemDataDTO>,
)
So the output would be a List<PickingList>.
There's nothing special about it really! groupBy takes a keySelector function which returns some value to be used as a key for that item. So if you want to match on two properties, that key item needs to be composed of those two values.
A Triple with two items is a Pair, so you can just do this:
// just FYI, "it.runId to it.pickingId" is shorthand for a Pair - you see it more
// when defining key/value pairs for maps though. "Pair(x, y)" might read better here
// since you're really just combining values, not describing how one relates to the other
items.groupBy { Pair(it.runId, it.pickingId) }
So each item will produce a Pair with those two values. Any other items with a Pair that matches (as far as the equals function goes) will be put into the same group. It's a bit like adding to a Map, except that if a key already exists, the value is added to a list instead of overwriting the previous value.
You can do that with any key really. Pair and Triple are just quick, general convenience classes for bundling a few items together - but a lot of the time it's better to define your own data structure, e.g. using a data class. So long as two instances with the same data are equal, they count as the same key for grouping.
As for the output you want, with the PickingList... you could use something like that for your grouping operation - but in that case you'd have to pretty much reimplement groupBy yourself. You'd have to take an item, and work out its composite key from the properties you want to consider. Then you'd need to find a match for that key in some store you've created for your groups
If it's a list of PickingLists, you'd need to go through each one, comparing its IDs to the ones you want, adding to its list if you find a match and creating the object if you can't find it.
If you're storing a map of Pair(id1, id2) -> PickingList then that's close to how groupBy works anyway, in terms of generating a key for lookups. In that case, you might want to just use groupBy to group all your items, and then transform the final map:
items.groupBy { Pair(it.runId, it.pickingId) }
.map { (ids, list) ->
PairingList(runId = ids.first, pickingId = ids.second, items = list)
}
This takes every map entry (a Pair of IDs and the list of all things grouped by those IDs) and uses it to create a PairingList from that key/value data. Basically, once you've grouped all your data, you transform it into the data structures you want to work with.
This is also a good example of why your own data class might be better than just using a Pair - it.first doesn't really tell you what that value is in the Pair, just that it's the first of the two values. Whereas
data class IdCombo(val runId: String, val pickingId: String)
works the same as a Pair, but the properties have useful names and make your code much more readable and less prone to bugs:
map { (ids, list) ->
// didn't even bother with the named arguments, since the names are in
// the ids object now!
PairingList(ids.runId, ids.pickingId, items = list)
}

Replacement for struct in Kotlin and how to store the data in the run time

I have to store and update the below variables in Kotlin
string name;
Array of Class Objects(5)
Array of Int(5)
C++ format:
struct subject
{
string name;
Array of Class Objects(5)
Array of Int(5)
};
vector<subject> sub;
In other programming languages C/C++ for ex, we use struct and put everything above in that.
Questions:
How to store and update above values with mixture of different types like Array, string, etc., in Kotlin?
Arrays will not get updated in one stretch. Ex: When someone calls AIDL interface with name, I create instance of class and stored the object in array of class obj(0) and integer array(0) as well updated with some value.
When the same AIDL interface is called with same name again, second instance of class will be created and store in **array of class obj(1)**and integer array(1) as well updated. As name is same, there is no need to update it again.
How to check the name and update the other arrays in the run time?
An additional use case, I need to make vector of that struct(according to C++). How I can achieve this in Kotlin?
Instead of a struct you would use a class in Kotlin: https://kotlinlang.org/docs/classes.html. There are several differences between the two that are relevant:
The declaration and class members and there implementation are done in the same place.
The constructor declaration is built into the class declaration.
Kotlin leans towards immutability. While you can reassign fields more often you will see val (like const) and immutable collections.
With that said, you would do something like this to implement your struct in Kotlin. The following isn't a literal 1 for 1 translation, but rather how you might solve your problem with idiomatic Kotlin:
class Subject(val name: String) {
val objects = mutableListOf<NameOfThatClass>()
val numbers = mutableListOf<Int>()
}
What's going on in that code snippet is that we are declaring a class Subject. It has a constructor that takes one argument called name of type String. The val keyword means that the argument will also be kept as a member variable, and that member variable cannot be reassigned. Next, in the class body, we declare and assign two more member variables. objects and numbers will also not be reassignable because of the val keyword, but instead of receiving a constructor argument as a value they receive the result of calling mutableListOf(), which creates more or less the equivalent of a vector. We could also use arrayOfNulls(5) and arrayOfInt(5), but unless you very specifically need fixed-sized arrays it's easier and more common to use lists in Kotlin.
You would then use it like so:
val myName = "foo"
val myFirstObject = ...
val myFirstNumber = 1
val mySubject = Subject(myName)
mySubject.objects += myFirstObject
mySubject.numbers += myFirstNumber
The += you see there isn't an actual reassignment, but an operator overload that acts as Kotlin's equivalent of std::vector's push_back(): https://kotlinlang.org/docs/collection-write.html#adding-elements.
Finally, as mentioned above, Kotlin's lists are what you would normally use in place of vector. However, it sounds like you want to be able to look up a specific entry by name, which is more efficient to do with a map https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-map/. You could do something like this:
val myMap = mutableMapOf<String, Subject>()
// add to the map like this
myMap[name] = Subject(name)
// get from the map like this (returns null if not in the map)
val mySubject = myMap[name]
// check if the subject is already in the map like this
myMap.containsKey(name)
Then, if you need to iterate over all the Subjects like you would with a vector, you can use myMap.values to get just the Subjects.

Recursively building a data class in Kotlin

I have am trying to create a recursive data class like so:
data class AttributeId (
val name: String,
val id: Int,
val children: List<AttributeId>?
)
The thing I'm struggling with now is building the data class by iterating over a source object.
How do I recursively build this object?? Is a data class the wrong solution here?
EDIT: Some more information about the Source object from which I want to construct my data class instance
The source object is a Java Stream that essentially* has the following shape:
public Category(final String value,
final Integer id,
final List<Category> children) {
this.value = value;
this.id = id;
this.children = children;
}
(For brevity the fields I don't care about have been removed from example)
I think I need to map over this stream and call a recursive function in order to construct the AttributeId data class, but my attempts seem to end in a stack overflow and a lot of confusion!
I don't think there's anything necessarily wrong with a data class that contains references to others.
There are certainly some gotchas.  For example:
If the list were mutable, or if its field was mutable (i.e. var rather than val), then you'd have to take care because its hashcode &c could change.
And if the chain of links could form a loop (i.e. you could follow the links and end up back at the original class), that could be very dangerous.  (E.g. calling a method such as toString() or hashCode() might either get stuck in an endless loop or crash the thread with a StackOverflowError.  You'd have to prevent that by overriding those methods to prevent them recursing.)  But that couldn't happen if the list and field were both immutable.
None of these issues are specific to data classes, though; a normal class could suffer the same issues (especially if you overrode methods like toString() or hashCode() without taking care).  So whether you make this a data class comes down to whether it feels like one: whether its primary purpose is to hold data, and/or whether the automatically-generated methods match how you want it to behave.
As Tenfour04 says, it depends what you're constructing these from.  If it naturally forms a tree structure, then this could be a good representation for it.
Obviously, you wouldn't be able to construct a parent before any of its children.  (In particular, the first instance you create would have to have either null or an empty list for its children.)  This would probably mean traversing the source in post-order.  The rest should fall out naturally from that.

Confused about the following line #field:[Expose SerializedName("id")]

I have the following data class that will retrieve data from an API:
data class Users(
#field:[Expose SerializedName("id")]
val id: Int)
I am just wondering what the #field: means.
Normally, I have always done like this:
data class Users(
#Expose
#SerializedName("id")
val id: Int)
I understand the meaning of expose and serializedName.
Just a few questions:
My best guess would be for the #field:[] would be to take an array of annotations, instead of putting them on each line as in the second example?
But is the field a Kotlin keyword or an annotation as it's preceded by the #?
Where else could you use the #field?
The val id in your example is declaring several different things in one go:
A constructor parameter.
A property of the class, implemented as a getter method.
A backing field for the property.
So which of those does an annotation get applied to?  It defaults to the parameter, and that's what your second example does.
If you want it to apply to the field instead, as in your first example, you use the field: target.
(It usually applies to single annotations, but it can apply to an array of them, as in this case.)
For more details, see the link jonrsharpe provided: https://kotlinlang.org/docs/reference/annotations.html#annotation-use-site-targets
The field:, property:, file:, &c targets are only for use with annotations.  (field is also a keyword within getter/setter definitions.)

Is returning pair from method considered a good practice in kotlin?

I have a complex method that doesn't get more complex when I gain two different values inside the method. They're substracted from the same lib class, but my use of them will not be connected anymore in my code.
I also don't want to have them declared as class fields.
Therefore my first idea is to return them as Pair because that means I don't need to call a very similar complex method twice. But on the other hand, it seems strange to return two unconnected values in Pair structure.
My question is more general: Is returning a pair of two unconnected values (from my class point of view) considered a good practice?
I am in kotlin, so if there are differences between languages, I need the answer specifically for kotlin.
There's nothing wrong with returning a Pair.
But since Kotlin can create a simple data class in one line, it's often better to return one of those instead.  That way, the two values have descriptive names instead of just ‘first’ and ‘second’, and you don't need to remember which order they're in.  For example:
data class Person(val name: String, val age: Int)
fun findPerson(): Person {
// …
return Person(name, age)
}
fun main() {
val person = findPerson()
println("Name = ${person.name}, age = ${person.age}.")
}
(Of course, this is a contrived example, in which the returned values have an obvious relationship.  But even when they don't, an ‘XxxResult’ data class can be easier to use than a Pair.)