Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin, difference between two lists of objects

could anyone help me to find a tiny solution to find the difference between two lists that contain objects? For example, I have lists:

data class Foo(val id: Int, val name: String)
data class Bar(val anotherId: Int, val name: String)

val first = listOf(Foo(1, "one"), Foo(2, "two"))
val second = listOf(Bar(1, "one"))

I need to get a result list, that contains the "difference" between the first collection and the second, mapped to the id's of the resulting collection, such that I can assert:

assert(result == listOf(2))

Where result is the result of the operation for which the answer to this question will provide.

Tried to do it with multiple mapping of id's but that's not very good solution, i guess...

val firstIds = first.map { it.id }.toList()
val secondIds = second.map { it.anotherId }.toList()
val difference = firstIds.minus(secondIds).toList()

UPD: i've forgot, that i have different id object keys, my bad, sorry

like image 437
darth jemico Avatar asked Mar 04 '26 15:03

darth jemico


1 Answers

You were almost there, assuming your lists are parameterized with the same type, which they appear to be in your question, then you can just call asSequence on the first collection, then minus on the resulting sequence, passing in the second collection, then map the resulting sequence to a sequence of the ids, before enumerating the sequence to a list with toList:

first.asSequence().minus(second).map { it.id }.toList()

Using asSequence will allow you to construct a sequence of operations on the original collection, meaning you won't iterate it twice due to the minus call and the map call. This sequence of operations will be evaluated, passing the result from each operation into the next, when a terminal operation is called on the sequence.

In this case, our terminal operation is toList() however there are several more which you can find by reading the source code of _Collections.kt

For some reason, someone downvoted my answer, really not sure why as they haven't left a comment.

EDIT:

The OP editted the question such that this answer is now invalid...

So, the updated question is how to find the "difference" between 2 lists that are parameterized with different types.

Well, that depends on what you consider the "difference" between these 2 types.

  1. if the ids and names are different?
  2. if the ids or names are different?
  3. if it's just the ids are different?
  4. if just the names are different?

Case 1:

first.asSequence().filter { f ->
   second.none { s ->
      s.anotherId == f.id && s.name == f.name
   }
}.map { f -> f.id }.toList()

Case 2:

first.asSequence().filter { f ->
   second.none { s ->
      s.anotherId == f.id || s.name == f.name
   }
 }.map { f -> f.id }.toList()

Case 3:

first.asSequence().filter { f ->
   second.none { s ->
      s.anotherId == f.id
   }
}.map { f -> f.id }.toList()

Case 4:

first.asSequence().filter { f ->
   second.none { s ->
      s.name == f.name
   }
}.map { f -> f.id }.toList()

All of these solution could cause n * m cycles however, although I can't think of a more optimal way to do this. Having said that, the original code I posted could also result in n * m cycles.

Tests to prove:

@Test
fun testCase1() {
    val result = case1()
    assert(result == listOf(2))
}

@Test
fun testCase2() {
    val result = case2()
    assert(result == listOf(2))
}

@Test
fun testCase3() {
    val result = case3()
    assert(result == listOf(2))
}

@Test
fun testCase4() {
    val result = case4()
    assert(result == listOf(2))
}
like image 96
Thomas Cook Avatar answered Mar 07 '26 06:03

Thomas Cook