使用带有可为空键的映射?

发布于 2025-01-19 19:45:12 字数 899 浏览 0 评论 0原文

(Kotlin 新手)我正在编写一个函数来记录两个列表之间的差异,如下所示:

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)}
        }
    }
}

这个想法是,如果 booksOne 或 booksTwo 中的一本书为空,那仍然是我想要的差异捕捉。但正如它所写的,我意识到如果我的映射中的键可以为空,我怎么能查找结果呢?

有没有一种方法可以重构它来记录一个列表具有空对象而另一个列表没有的实例,或者我是否以错误的方式看待这个问题?

(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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

笔芯 2025-01-26 19:45:12

即使使用空键,您的代码也可以很好地工作。考虑这个完整的Kotlin程序:

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") }
        }
    }
}

这将打印以下内容:

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)

如您所见,它也将获取NULL书籍

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

余生一个溪 2025-01-26 19:45:12

这不是问题的答案,因为我并不真正了解问题是什么。也许可以使用内置的收集功能来播放:

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

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
顾铮苏瑾 2025-01-26 19:45:12

如果我有正确的话,您有两个具有id属性的书籍列表,您想比较这些列表以查找:

  • 共享ID的书籍
  • 与其他书籍 在两个列表中出现的一本列表
  • 具有不同的ID

书中,但仍需要考虑 和书籍,即具有 null ID它出现在两个列表中,但是其中一个ID为无效(即不同的ID)。

我不知道是否有两本具有无效ID的书籍将被视为“两本不同的书籍共享ID”,我想不!


因此,您的问题是该ID是否应该是唯一的,但是它也可以无效,除非您肯定只有一个id =,否则您不能将其用作密钥。每个列表中的null。因此,您不能将ID用作查找。真的,您在那里做的不是存储书籍 - 您正在存储ID,而是将其映射到每本书的单书。而且,既然您期望多本书拥有相同的ID(包括NULL),那么对吧?

我能想到一些选择。首先,带有不同id的简单相同的书籍,理想情况下,就equals()而言而言并不相等。不同的数据,对吗?如果是这样,您只需找到仅出现在其中一个列表中的所有book(如果它们在其他列表中,它们都不匹配):(

Yoinking Ivo Becker的设置代码,谢谢!)

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"),
    Book(null, "Bad Book")
)

fun main() {
    // or (list1 subtract list2) union (list2 subtract list1) if you like
    val suspects = (list1 union list2) subtract (list1 intersect list2)
    println(suspects)
}

>> [Book(key=1, title=Book A), Book(key=3, title=Book C), Book(key=4, title=Book D), 
Book(key=null, title=Book NullKey), Book(key=null, title=Bad Book)]

这只是使用一些 set set操作 要查找两个列表中不存在的所有项目(相交是两者中的内容,联合就是一切)。

如果您需要将它们分开,以便每个列表都可以记录,则可以执行类似的操作:

list1.filterNot { it in list2 }.forEach { println("List one: $it") }
list2.filterNot { it in list1 }.forEach { println("List two: $it") }

>>> List one: Book(key=1, title=Book A)
List one: Book(key=3, title=Book C)
List two: Book(key=4, title=Book D)
List two: Book(key=null, title=Book NullKey)
List two: Book(key=null, title=Bad Book)

如果您出于任何原因无法执行此操作(book,具有不同的IDS返回true true for 等于),然后您可以做这样的事情:

fun List<Book>.hasSameBookWithSameId(book: Book) =
    firstOrNull { it == book }
        ?.let { it.key == book.key }
        ?: false
list1.filterNot(list2::hasSameBookWithSameId).forEach { println("List one: $it")}
list2.filterNot(list1::hasSameBookWithSameId).forEach { println("List two: $it")}

If I've got this right, you have two lists of Books which have an id property, and you want to compare those lists to find:

  • books that share an ID with a different book
  • books that only appear in one list
  • books that appear in both lists, but with different IDs

And books with a null ID still need to be considered, i.e. if it only appears in one list (with a null ID), or if it appears in both lists, but the ID is null for one of them (i.e. different IDs).

I don't know if two different books with null IDs would be considered as "two different books sharing an ID", I'll guess no!


So your problem is if that ID is supposed to be unique, but it's also nullable, you can't use it as a key unless you're sure there will only be one with ID=null in each list. So you can't use the ID as a lookup. Really what you're doing there isn't storing Books - you're storing IDs, and the single book that maps to each. And since you're expecting multiple books to have the same ID (including null) that's not gonna work, right?

There's a couple of options I can think of. First the easy one - the same Book with a different ID is, ideally, not equal as far as equals() is concerned. Different data, right? If that's the case, you can just find all the Books that only appear in one of the lists (i.e. if they are in the other list, they don't match):

(yoinking Ivo Becker's setup code, thanks!)

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"),
    Book(null, "Bad Book")
)

fun main() {
    // or (list1 subtract list2) union (list2 subtract list1) if you like
    val suspects = (list1 union list2) subtract (list1 intersect list2)
    println(suspects)
}

>> [Book(key=1, title=Book A), Book(key=3, title=Book C), Book(key=4, title=Book D), 
Book(key=null, title=Book NullKey), Book(key=null, title=Bad Book)]

That's just using some set operations to find all the items not present in both lists (the intersect is the stuff that's in both, union is everything).

If you need to keep them separate so you can log per list, you can do something like this:

list1.filterNot { it in list2 }.forEach { println("List one: $it") }
list2.filterNot { it in list1 }.forEach { println("List two: $it") }

>>> List one: Book(key=1, title=Book A)
List one: Book(key=3, title=Book C)
List two: Book(key=4, title=Book D)
List two: Book(key=null, title=Book NullKey)
List two: Book(key=null, title=Bad Book)

If you can't do that for whatever reason (Books with different IDs return true for equals) then you could do something like this:

fun List<Book>.hasSameBookWithSameId(book: Book) =
    firstOrNull { it == book }
        ?.let { it.key == book.key }
        ?: false
list1.filterNot(list2::hasSameBookWithSameId).forEach { println("List one: $it")}
list2.filterNot(list1::hasSameBookWithSameId).forEach { println("List two: $it")}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文