Home > Software engineering >  Using a Map with Nullable Keys?
Using a Map with Nullable Keys?

Time:04-06

(new to Kotlin) I am writing a function to log differences between two Lists, like so:

fun logDifferences(booksOne: List<Book>?, booksTwo: List<Book>?, logEntryFactory: LogEntryFactory) {

    val booksOneByKey: Map<String?, Book> = booksOne?.associateBy({ it.key }, { it }) ?: mapOf()
    val booksTwoByKey: Map<String?, Book> = booksTwo?.associateBy({ it.key }, { it }) ?: mapOf()
    val allBookKeysSet: Set<String?> = booksOneByKey.keys.union(booksTwoByKey.keys)

    allBookKeysSet.forEach {
        val bookOne = booksOneByKey[it]
        val bookTwo = booksTwoByKey[it]

        if (bookOne != bookTwo) {
            bookOne?.let { // log the book) }
            bookTwo?.let { // log the book)}
        }
    }
}

The idea is that if a book from booksOne or booksTwo is null, that is still a difference that I would like to capture. But as it is written, I am realizing that if a key can be nullable in my map, how could I even look up the result?

Is there a way of refactoring this to log instances where one list has a null object and not the other, or am I looking at this the wrong way?

CodePudding user response:

Your code works perfectly fine even with null keys. consider this full kotlin program:

data class Book(
    val key: String?,
    val title: String
)
val list1 = listOf(
    Book("1", "Book A"),
    Book("2", "Book B"),
    Book("3", "Book C"),
)

val list2 = listOf(
    Book("2", "Book B"),
    Book("4", "Book D"),
    Book(null, "Book NullKey"),
)


fun main() {
    val booksOneByKey: Map<String?, Book> = list1?.associateBy({ it.key }, { it }) ?: mapOf()
    val booksTwoByKey: Map<String?, Book> = list2?.associateBy({ it.key }, { it }) ?: mapOf()
    val allBookKeysSet: Set<String?> = booksOneByKey.keys.union(booksTwoByKey.keys)

    allBookKeysSet.forEach {
        val bookOne = booksOneByKey[it]
        val bookTwo = booksTwoByKey[it]
        println("key $it :")
        if (bookOne != bookTwo) {
            bookOne?.let { println("check $it") }
            bookTwo?.let { println("check2 $it") }
        }
    }
}

this will print the following:

key 1 :
check Book(key=1, title=Book A)
key 2 :
key 3 :
check Book(key=3, title=Book C)
key 4 :
check2 Book(key=4, title=Book D)
key null :
check2 Book(key=null, title=Book NullKey)

as you can see it will also take the null book

CodePudding user response:

This is not an answer to the question, as I do not really understand what the issue is. Maybe look and play around with the built-in collection functions:

data class Book(
  val key: String,
  val title: String
)

val list1 = listOf(
  Book("1", "Book A"),
  Book("2", "Book B"),
  Book("3", "Book C")
)

val list2 = listOf(
  Book("2", "Book B"),
  Book("4", "Book D")
)

val all = (list1   list2).distinct()   // same as: (list1.plus(list2)).distinct()
println(all)   // Books 1, 2, 3, 4

val inBoth = list1.intersect(list2)
println(inBoth)   // Book 2

val inList1Only = list1.minus(list2)
println(inList1Only)   // Books 1, 3

val inList2Only = list2.minus(list1)
println(inList2Only)   // Book 4
  • Related