how to use distinct operator - kotlin

would you please let me know the reason why I am getting the output mentioned below??
I expected to get the following out put:
[Person(_name=xyz4, _age=30),
Person(_name=xyz5, _age=50)]
because of distinct operator
Main:
fun main(args: Array<String>) {
val person1 = Person("xyz1", 10);
val person2 = Person("xyz2", 20);
val person3 = Person("xyz3", 30);
val person4 = Person("xyz4", 30);
val person5 = Person("xyz5", 50);
var persons = listOf(
person1, person2, person3, person4 , person5)
.asSequence()
.filter { x-> x.age >=30 }
.distinct()
println(persons.toList())
}
output:
[Person(_name=xyz3, _age=30), Person(_name=xyz4, _age=30),
Person(_name=xyz5, _age=50)]

you can use
.distinctBy { it.age }
instead of
.distinct()
if order of elements is important you can add sortedBy like this
fun main(args: Array<String>) {
val person1 = Person("xyz1", 10)
val person2 = Person("xyz2", 20)
val person3 = Person("xyz3", 30)
val person4 = Person("xyz4", 30)
val person5 = Person("xyz5", 50)
val persons = listOf(
person1, person2, person3, person4, person5)
.reversed()
.filter { x -> x.age >= 30 }
.distinctBy { it.age }
.sortedBy { it.age }
println(persons.toList())
}

Related

kotlin how to remove duplicate through some value in object array?

how to remove duplicate through some value in object array?
data class Person(
val id: Int,
val name: String,
val gender: String
)
val person1 = Person(1, "Lonnie", "female")
val person2 = Person(2, "Noah", "male")
val person3 = Person(3, "Ollie", "female")
val person4 = Person(4, "William", "male")
val person5 = Person(5, "Lucas", "male")
val person6 = Person(6, "Mia", "male")
val person7 = Person(7, "Ollie", "female")
val personList = listOf(person1,person2,person3,person4,person5,person6,person7)
Person 3 and person 7 have a "female" gender and have the same name. So person7 needs to be removed.
But "male" gender can have duplicated name.
And the order of the list must be maintained.
expect result
[
Person(1, "Lonnie", "female"),
Person(2, "Noah", "male"),
Person(3, "Ollie", "female"),
Person(4, "William", "male"),
Person(5, "Lucas", "male"),
Person(6, "Mia", "male"),
]
You could do something like this, assuming the order is indicated by the id field of the Person class:
val personList = listOf(person1,person2,person3,person4,person5,person6,person7)
.partition { it.name == "male" }
.let { (males, females) -> males + females.distinctBy { it.name } }
.sortedBy { it.id }
I believe this does what you want:
val result = personList.filter {
person -> person.gender == "male" || (personList.first {
person.name == it.name && person.gender == it.gender
} == person)
}

Deep merging data classes in Kotlin

How can I do a recursive / deep merge of two data classes in Kotlin? Something like this:
import kotlin.reflect.*
import kotlin.reflect.full.*
data class Address(
val street: String? = null,
val zip: String? = null
)
data class User(
val name: String? = null,
val age: Int? = null,
val address: Address? = null
)
inline fun <reified T : Any> T.merge(other: T): T {
val nameToProperty = T::class.declaredMemberProperties.associateBy { it.name }
val primaryConstructor = T::class.primaryConstructor!!
val args = primaryConstructor.parameters.associate { parameter ->
val property = nameToProperty[parameter.name]!!
val type = property.returnType.classifier as KClass<*>
if (type.isData) {
parameter to this.merge(other) //inline function can't be recursive
} else {
parameter to (property.get(other) ?: property.get(this))
}
}
return primaryConstructor.callBy(args)
}
val u1 = User(name = "Tiina", address = Address(street = "Hämeenkatu"))
val u2 = User(age = 23, address = Address(zip = "33100"))
u1.merge(u2)
// expected: User(age = 23, name= "Tiina", address = Address(zip = "33100", street = "Hämeenkatu")
related: Combining/merging data classes in Kotlin
There were several problems in the posted code,
unnecessary reification and inlining
when type isData was detected instead of merging the values of the property merge on this with the other was called, so it became endless recursion.
get cannot be used on KProperty1<out T, Any?> because of the variance
some non-idiomatic stuff which works, but can be made better
Here's the fixed version. For production I would've added some checks and error messages, but this should work for "happy path" and hopefully give you the base to build on:
import kotlin.reflect.KClass
import kotlin.reflect.KParameter
import kotlin.reflect.KProperty1
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.full.primaryConstructor
data class Address(
val street: String? = null,
val zip: String? = null
)
data class User(
val name: String? = null,
val age: Int? = null,
val address: Address? = null,
val map: Map<String, Int>? = null
)
fun <T> mergeData(property: KProperty1<out T, Any?>, left: T, right: T): Any? {
val leftValue = property.getter.call(left)
val rightValue = property.getter.call(right)
return rightValue?.let {
if ((property.returnType.classifier as KClass<*>).isSubclassOf(Map::class)) (leftValue as? Map<*, *>)?.plus(it as Map<*, *>)
else leftValue?.merge(it)
} ?: rightValue ?: leftValue
}
fun <T> lastNonNull(property: KProperty1<out T, Any?>, left: T, right: T) =
property.getter.call(right) ?: property.getter.call(left)
fun <T : Any> T.merge(other: T): T {
val nameToProperty = this::class.declaredMemberProperties.associateBy { it.name }
val primaryConstructor = this::class.primaryConstructor!!
val args: Map<KParameter, Any?> = primaryConstructor.parameters.associateWith { parameter ->
val property = nameToProperty[parameter.name]!!
val type = property.returnType.classifier as KClass<*>
when {
type.isData || type.isSubclassOf(Map::class) -> mergeData(property, this, other)
else -> lastNonNull(property, this, other)
}
}
return primaryConstructor.callBy(args)
}
// verification
val u1 = User(name = "Tiina", address = Address(street = "Hämeenkatu"), map = mapOf("a" to 1))
val u2 = User(age = 23, address = Address(zip = "33100"), map = mapOf("b" to 2))
check(
u1.merge(u2) == User(
age = 23,
name = "Tiina",
address = Address(zip = "33100", street = "Hämeenkatu"),
map = mapOf("a" to 1,"b" to 2)
)
) {
"doesn't work"
}
println("Works!")

Handling lists of two different types with same code using functional programming in kotlin

I have two lists with different types list1 and list2 . I have a method which does the same operation on the lists.
I'm using lambdas where I cannot access the property as (it.prop1) if I'm using List of type Any.
Is there any solution to avoid this issue with lambdas?
val list1: List<Student> = ..
val list2: List<Teacher> = ..
list1.filter {
school.contains(it.prop1) }
.forEach {
total += it.prop2.toLong()
}
list2.filter {
school.contains(it.prop1) }
.forEach {
total += it.prop2.toLong()
}
Thanks.
Try this:
object Test {
private fun isContains(school: Set<Int>, any: Any) = when (any) {
is Student -> school.contains(any.prop1)
is Teacher -> school.contains(any.prop1)
else -> false
}
private fun value(any: Any) = when (any) {
is Student -> any.prop2
is Teacher -> any.prop2
else -> throw NoWhenBranchMatchedException("any should be Student or Teacher")
}
#JvmStatic
fun main(args: Array<String>) {
val school = setOf(1, 2)
val list = listOf(Student(1, 1), Student(2, 2), Student(3, 3), Teacher(1, 1), Teacher(2, 2), Teacher(3, 3))
val total = list.filter {
isContains(school, it)
}.map {
value(it)
}.sum()
println("Total: $total")
}
private class Student(val prop1: Int, val prop2: Int)
private class Teacher(val prop1: Int, val prop2: Int)
}
You may use Type Checks and Casts
class Student(val prop1:Int, val prop2:Int)
class Teacher(val prop1:Int, val prop2:Int)
val list : List<Any> = listOf(Student(1,1),Student(2,2),Student(3,3),Teacher(1,1),Teacher(2,2),Teacher(3,3))
var total : Long = 0
val school : Array<Int> = arrayOf(1,2)
list.filter{
if(it is Student)
{
school.contains((it as Student).prop1)
}
else if(it is Teacher)
{
school.contains((it as Teacher).prop1)
}
else
{
false
}
}.forEach{
if(it is Student)
{
total += (it as Student).prop2.toLong()
}
else if(it is Teacher)
{
total += (it as Teacher).prop2.toLong()
}
}
println(total) //print 6 in this example
This is ugly tough. It is better to make Student and Teacher either inherit a common superclass or implement a common interface
As far as I know you can't. You can take advantage of common interface.
For example:
interface Human{
val age: Int
}
class Student(override val age: Int): Human
class Teacher(override val age: Int, val salary: Double):Human
fun x(){
val list1: List<Student> = ...
val list2: List<Teacher> = ...
val school: List<Human> = ...
val result = school
.filter { it is Student }
.sumBy { it.age}
val result2 = school
.filter { it is Teacher }
.sumBy { it.age }
}

contents of a list after filter

would you please let me how can I print out the contents of persons?
the output of the code is
[Person#72ea2f77, Person#33c7353a, Person#681a9515, Person#3af49f1c,
Person#19469ea2]
code:
fun main(args: Array<String>) {
val person1 = Person("xyz1", 10);
val person2 = Person("xyz2", 20);
val person3 = Person("xyz3", 30);
val person4 = Person("xyz4", 40);
val person5 = Person("xyz5", 50);
var persons = listOf(
person1, person2, person3, person4 , person5)
.asSequence()
.filter { x-> x.age >=30 }
println(persons.toList())
}
You can either implement toString() method in the Person class or you can make Person class a data class.
One easy way to show the contents of the Person class is to override the toString method in the Persons class. Here is a working example:
fun main(args: Array<String>) {
val person1 = Person("xyz1", 10);
val person2 = Person("xyz2", 20);
val person3 = Person("xyz3", 30);
val person4 = Person("xyz4", 40);
val person5 = Person("xyz5", 50);
var persons = listOf(
person1, person2, person3, person4, person5)
.asSequence()
.filter { x -> x.age >= 30 }
println(persons.toList())
}
class Person constructor(val name: String, val age: Int) {
override fun toString(): String {
return "$name is $age years old."
}
}
Will output:
[xyz3 is 30 years old., xyz4 is 40 years old., xyz5 is 50 years old.]

How do I use "for" for this case

I need this function to look in a file for a certain day, then say for each flight how many people are there, in the same day.
So far I managed to get it to say how many people there are per day, but im struggling to get it to say how many people are on each flight per day.
fun interval(reserves: ArrayList<Reserves>, dayInferior: Int, daySuperior: Int) {
var count = dayInferior
var person: Int
for (reserve in reserves) {
person = 0
for (reserve in reserves) {
if (reserve.day == count && reserve.day <= daySuperior) {
person++
}
}
if (count <= daySuperior) {
println("${reserve.numFlight} Day $count has $person")
}
count++
}
}
class Reserves {
var numCc: Int?
var name: String
var numFlight: String
var day: Int
constructor(numCc: Int?, name: String, numFlight: String, day: Int) {
this.numCc = numCc
this.name = name
this.numFlight = numFlight
this.day = day
}
}
fun readFileReserves(fileNameInput: String): ArrayList<Reserves> {
val reserves: ArrayList<Reserves> = ArrayList()
val lines = File(fileNameInput).readLines()
for (line in lines) {
val parts = line.split(':')
val numCc = parts[0].toIntOrNull()
val name = parts[1]
val numFlight = parts[2]
val day = parts[3].toIntOrNull()
if (day == null || numCc == null) {
println("Invalid day")
} else {
val r = Reserves(numCc, name, numFlight, day)
reserves.add(r)
}
}
return reserves
}
I assume #Jocas means interval to get the count of Reserves between dayInferior and daySuperior.
fun interval(reserves: List<Reserves>, dayInferior: Int, daySuperior: Int) {
val map = mapReserves(reserves)
for(day in dayInferior..daySuperior) {
map.forEach {
val reservesName = it.key.first
val reservesDay = it.key.second
val reservesCount = it.value.count()
if (reservesDay == day)
print("$reservesName has $reservesCount on day passengers $day")
}
}
}
fun mapReserves(reserves: List<Reserves>): Map<Pair<String, Int>, List<Reserves>> {
val map = mutableMapOf<Pair<String, Int>, MutableList<Reserves>>()
reserves.forEach {
val key = it.numFlight to it.day
val list = map[key] ?: mutableListOf()
list.add(it)
map[key] = list
}
return map
}
// This is how you write in Kotlin. Your style is Java.
class Reserves(var numCc: Int, var name: String, var numFlight: String, var day: Int)
// Use abstraction List and MutableList instead of ArrayList
fun readFileReserves(fileNameInput: String): List<Reserves> {
val reserves = mutableListOf<Reserves>()
val lines = File(fileNameInput).readLines()
for (line in lines) {
val parts = line.split(":")
val numCc = parts[0].toIntOrNull()
val name = parts[1]
val numFlight = parts[2]
val day = parts[3].toIntOrNull()
if (day == null || numCc == null) {
println("Invalid day")
} else {
val r = Reserves(numCc, name, numFlight, day)
reserves.add(r)
}
}
return reserves
}