【问题标题】:Get uncommon elements from two list - KOTLIN从两个列表中获取不常见的元素 - KOTLIN
【发布时间】:2018-07-08 00:44:30
【问题描述】:

我有两个相同模型类(STUDENT)的列表,示例学生对象结构如下,

{
  "_id": "5a66d78690429a1d897a91ed",
  "division": "G",
  "standard": "X",
  "section": "Secondary",
  "lastName": "Sawant",
  "middleName": "Sandeep",
  "firstName": "Shraddha",
  "pin": 12345,
  "isEditable": true,
  "isTracked": false
}

一个列表有 3 个对象,其他 2 个。假设列表 A 有 1、2、3 个学生,列表 B 有 1、2 个

所以我的问题是,是否有任何内置函数可以通过仅比较 id 来获取不常见元素 ?如果不是,我该如何解决这个问题。

仅供参考,以下是我解决的两种方法,但失败得很惨。

方法 1。

internal fun getDistinctStudents(studentsList: List<Students>, prefStudents: List<Students>): List<Students> {
    val consolidated = prefStudents.filter {
        prefStudents.any { students: Students -> it._id == students._id }
    }
    return prefStudents.minus(consolidated)
}

方法 2。

internal fun getDistinctStudents(studentsList: List<Students>, prefStudents: List<Students>): List<Students> {
    val consolidatedStudents = studentsList + prefStudents
    val distinctStudents = consolidatedStudents.distinctBy{ it._id }
    return prefStudents.minus(distinctStudents)
}

我们将不胜感激任何形式的帮助。

谢谢

【问题讨论】:

  • 合并两个列表,然后制作一个从 String 到 Integer 的 hashmap,这是学生的 id 到出现次数,然后遍历 hashmap 并获取只有 1 次出现的键。跨度>
  • 您可以创建从学生到出现次数的地图,只是为了最终得到学生列表

标签: android list kotlin


【解决方案1】:

在有人想出一个更简洁、更简洁的解决方案之前,我认为这是一个很容易阅读的可行方案:

internal fun getDistinctStudents(studentsList: List<Students>, prefStudents: List<Students>): List<Students> {
    val studentsIds = studentsList.map { it._id }          // [ 1, 2, 3 ]
    val prefStudentIds = prefStudents.map { it._id }       // [ 1, 2 ]
    val commonIds = studentsIds.intersect(prefStudentIds)  // [ 1, 2 ]

    val allStudents = studentsList + prefStudents      // [ Student1, Student2, Student3, Student1, Student2 ]
    return allStudents.filter { it._id !in commonIds } // [ Student3 ]
}

如果您有大量学生(数百人),请考虑在各个步骤中使用序列,也许在连接最后两个列表之前进行过滤也会有所帮助:

val filteredStudents = studentsList.filter { it._id !in commonIds }
val filteredPrefStudents = prefStudents.filter { it._id !in commonIds }
return filteredStudents + filteredPrefStudents

编辑:见this answer instead

【讨论】:

  • 会尝试并通知您。感谢您的努力。 :)
  • 有人最终发布了一个不错的解决方案,请参阅here
  • 我找到了一个解决方案.. 请检查它是否是实现它的正确方法?如果您能检查解决方案并指出是否有问题,将不胜感激。
【解决方案2】:

这是使用 HashMap 的解决方案,代码可能会更好,但我对 kotlin 很陌生

fun getDistinctStudents(studentsList: List<Student>, prefStudents: List<Student>): List<Student> {
    val studentsOccurrences = HashMap<Student, Int>()
    val consolidatedStudents = studentsList + prefStudents
    for (student in consolidatedStudents) {
        val numberOfOccurrences = studentsOccurrences[student]
        studentsOccurrences.put(student, if(numberOfOccurrences == null) 1 else numberOfOccurrences + 1)
    }
    return consolidatedStudents.filter { student -> studentsOccurrences[student] == 1 }
}

你的学生类应该是一个数据类或至少覆盖哈希码和等于用作键。

【讨论】:

  • 会尽力让你知道。感谢您的努力。 :)
【解决方案3】:

实现 Ahmed Hegazy 所发布内容的更多 Kotlin 方式。该地图将包含元素列表,而不是键和计数。

使用 HashMap 和 Kotlin 内置插件。 groupBy 使用 Lambda 中定义的键(本例中为 id)和项目列表(本例中为列表)创建一个 Map

然后过滤掉列表大小不是 1 的条目。

最后,将其转换为单个学生列表(因此调用 flatMap)

val list1 = listOf(Student("1", "name1"), Student("2", "name2"))
val list2 = listOf(Student("1", "name1"), Student("2", "name2"), Student("3", "name2"))

val sum = list1 + list2
return sum.groupBy { it.id }
    .filter { it.value.size == 1 }
    .flatMap { it.value }

【讨论】:

  • 不错的解决方案! :)
  • 其实..是的..这确实是一个不错的解决方案,
  • 拯救了我的一天
  • 亲亲。把事情简单化。我喜欢它。
【解决方案4】:

在对 Kotlin 文档进行一些搜索之后,我终于找到了解决方案。我正在寻找的功能是filterNot

这是我尝试过的完整解决方案。

internal fun getDistinctStudents(studentsList: List<Students>, prefStudents: List<Students>): List<Students> {
    return prefStudents.filterNot { prefStudent ->
         studentsList.any {
             prefStudent._id == it._id
         }
    } 
}

返回不常见的元素。

【讨论】:

  • 这不会给你只在studentsList的学生,只给你只在prefStudents的学生。我认为你应该坚持@Mikezx6r 的解决方案。
【解决方案5】:

目前在移动设备上,因此我无法对其进行测试,但这可能会满足您的需求。 使用 stdlib 中的减法 https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/subtract.html

internal fun getDistinctStudents(studentsList: List<Students>, prefStudents: 
List<Students>): List<Students> {
    return prefStudents.subtract(studentList) + studentList.subtract(prefStudents)
}

【讨论】:

  • 感谢您的努力,但我所说的普通对象仅指学生 ID,只有 id 可以相同,其余所有数据可以或可能不同,所以如果我使用减去 case break if id is相同而名称不同。
  • 哦,我明白了。抱歉,我假设 Student 是一个数据类,而 id/name 是主要构造函数的一部分
  • 没有问题打扰.. 再次感谢您的努力。 :)
【解决方案6】:

我知道这是一篇旧帖子,但我相信有一个更简洁、更短的解决方案。请参阅下面的示例,使用 Mikezx6r 的数据,其答案已被上述接受。

val list1 = listOf(Student("1", "name1"), Student("2", "name2"))
val list2 = listOf(Student("1", "name1"), Student("2", "name2"), Student("3", "name2"))

val difference = list2.toSet().minus(list1.toSet())

【讨论】:

  • 这仅适用于您想要来自 list2 的元素,而这些元素不在 list1 中。它不是 2 个列表的“不同元素”的结果。 list1.toSet().minus(list2.toSet()) 没有给出任何元素,所以要小心使用这个答案。
【解决方案7】:

这是一个扩展功能,基本上可以满足您的需求。它假设元素E 知道如何被识别,例如Student._id 在您的示例中:

infix fun <E> Collection<E>.symmetricDifference(other: Collection<E>): Set<E> {
    val left = this subtract other
    val right = other subtract this
    return left union right
}

这是一个如何使用它的示例:

val disjunctiveUnion: List<Student> = listA symmetricDifference listB

我为其编写的示例测试用例:

@Test
fun `symmetric difference with one of either set`() {
    val left = listOf(1, 2, 3)
    val right = listOf(2, 3, 4)

    val result = left symmetricDifference right

    assertEquals(setOf(1, 4), result)
}

【讨论】:

  • 这是一个很棒的解释!我有一个案例,我只需要从让我们说左边删除冗余元素,例如从您的示例中,我希望结果是 (1) 的列表,其中左侧是 (1, 2, 3),右侧是 (2, 3, 4)
【解决方案8】:

如果您有两个列表,其中元素被标识,例如通过某种 id (item.id),然后您可以执行以下操作:

fisrtList.filter { it.id !in secondList.map { item -> item.id } }

我假设 firstList 和 secondList 自然包含相同类型的对象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-05
    • 1970-01-01
    • 2021-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多