Home > Software design >  How to compare two lists and insert values based on one into the other by a property in kotlin?
How to compare two lists and insert values based on one into the other by a property in kotlin?

Time:09-13

I am new to kotlin and I am trying to loop through two list and insert email in people.

data class User(val id:String,val name: String,val email: String)
data class Person(val id:String,val name: String, val email: String="")

//Input
val users = listOf(User("1","John","[email protected]"),User("2","Doe","[email protected]"))
val people = listOf(Person("1","John"),Person("2","Doe"))


//expected
val userToPerson =  listOf(Person("1","John","[email protected]"),Person("2","Doe","[email protected]"))

I am trying with this.

      
        val map = people.map { it ->
            {
                val foundUser = users.find { user -> user.id == it.id }
                if (foundUser != null) {
                    it.email = foundUser.email
                }
            }
        }
        map.forEach(System.out::print)

I am getting error for foundUser.isNotNull() here Unresolved reference: isNotNull

Updated with suggested:

() -> kotlin.Unit() -> kotlin.Unit

I am trying to convert a list of users to a list of people. They both have their ids as common.

I want to update people Person class corresponding to their user email.

All people do not have email. But all users have the email.

So, the final result would have people with email. If there is a person with no matching id, we can skip that data.

CodePudding user response:

First, calling find for every person is not only a bit awkward to write, it's also (which is far worse) inefficient. (It takes time proportional to the square of the number of people, which means it will perform really badly as the number of people gets large.)

To fix that, I'd create a map from an ID to its User:

val usersById = users.associateBy{ it.id }

We can then look up users by their ID quickly, in a way which scales well.

Armed with that, the solution can be fairly straightforward. Here's one which creates new* Person objects:

val userToPerson = people.map{ person ->
    val user = usersById[person.id]
    if (user != null && user.email != person.email)
        Person(person.id, person.name, user.email)
    else
        person
}

This solution is a little longer than necessary, but I hope it's easy to read and understand. It also avoids creating Person objects unless necessary, for efficiency. And when there's no corresponding User, it uses the existing Person.


* As the question is currently written, Person's fields are immutable, so the existing Person objects can't be updated with a new email address. That leads naturally into a functional style.

That's not necessarily a bad thing; immutability has many benefits, such as being easier to think about, and thread safety. It can also allow some compiler optimisations. However, if you're not careful, it can generate lots of temporary objects, which can can reduce efficiency (due to cache misses, constructor calls, and then more frequent garbage collections).

The alternative would be to make Person mutable, and do all the updates in-place — which is the traditional imperative style that most of us started from.

Both approaches are valid; which one you choose is a trade-off involving safety, maintainability, and performance — Kotlin supports both.

  • Related