从两个列表中获取不常见的元素 - KOTLIN

时间:2018-01-29 10:40:13

标签: android list kotlin

我有两个相同模型类的列表(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)
}

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

谢谢

8 个答案:

答案 0 :(得分:8)

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

使用HashMap和Kotlin内置插件。 groupBy创建一个Map,其中包含Lambda中定义的密钥(在本例中为id),以及项目列表(此方案的列表)

然后过滤出列表大小不是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 }

答案 1 :(得分: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

答案 2 :(得分:1)

这是使用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 :(得分:1)

最后,经过对Kotlin文档的一些搜索,我得到了解决方案。我正在寻找的功能是filterNot

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

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

返回了不常见的元素。

答案 4 :(得分:1)

现在在移动设备上,所以我无法测试它,但这可能适合您的需要。 使用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)
}

答案 5 :(得分:1)

这是一个扩展功能,基本上可以完成您想要的操作。假设元素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)
}

答案 6 :(得分:1)

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

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

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

答案 7 :(得分:0)

我知道这是一个过时的帖子,但我相信有一个更简洁,更短的解决方案。使用 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())