首页 > 解决方案 > Kotlin distinctBy 有条件

问题描述

我有多个具有相同键的对象的数组,其他对象有空值,我希望使用 distinctBy 删除重复项并获取值具有最长字符串的对象。

data class ObjA(val key: String, val value: String)

fun test() {
    val listOfA = mutableListOf(
            ObjA("one", ""), //This will be taken in distinctBy.
            ObjA("one", "o"),
            ObjA("one", "on"),
            ObjA("one", "one"), //This will be ignored in distinctBy. I WANT THIS ONE.

            ObjA("two", ""), //This will be returned in distinctBy
            ObjA("two", "2"),
            ObjA("two", "two"), //But I want this.

            ObjA("three", "3"),
            ObjA("four", "4"),
            ObjA("five", "five")
    )

    val listOfAWithNoDuplicates = listOfA.distinctBy { it.key }

    for (o in listOfAWithNoDuplicates) {
        println("key: ${o.key}, value: ${o.value}")
    }
}

输出

key: one, value: 
key: two, value: 
key: three, value: 3
key: four, value: 4
key: five, value: five

如何使这项工作。任何帮助将不胜感激。

标签: kotlin

解决方案


由于distinctBy只是根据您的选择器(并且按照列表的顺序)返回不同的键,因此您最终会得到唯一的键,但还没有您想要的值。

对于那个特定的用例,我可能只是预先排序,然后是distinctBy

listOfA.sortedByDescending { it.value.length }
       .distinctBy { it.key }

它会创建一个新列表,sortedByDescending或者您只需预先对当前列表进行排序 ( sortByDescending) 并distinctBy稍后应用,例如:

listOfA.sortByDescending { it.value.length }
listOfA.distinctBy { it.key }

在这两种情况下,您都会得到一个List<ObjA>具有预期值的新值。

我还想到了其他几个变体。所有这些变体都会将结果放入Map<String, ObjA>其中 key 实际上是 unique ObjA.key。如果您对 key/ -mapping.values不感兴趣,您可能想直接致电。ObjA

  1. 变体使用groupingByand reduce

    listOfA.groupingBy { it.key }
           .reduce { _, acc, e->
             maxOf(e, acc, compareBy { it.value.length })
           }
    
  2. 使用普通forEach/for并填充自己的变体Map

    val map = mutableMapOf<String, ObjA>()
    listOfA.forEach {
        map.merge(it.key, it) { t, u ->
            maxOf(t, u, compareBy { it.value.length })
        }
    }
    
  3. 变体使用foldand merge(与之前的非常相似......只是使用fold而不是for/ forEach):

    listOfA.fold(mutableMapOf<String, ObjA>()) { acc, objA ->
      acc.apply {
        merge(objA.key, objA) { t, u ->
          maxOf(t, u, compareBy { it.value.length })
        }
      }
    }
    
  4. 变体使用groupBy后跟mapValues(但您实际上是在创建 1 个您立即丢弃的地图):

    listOfA.groupBy { it.key } // the map created at this step is discarded and all the values are remapped in the next step
           .mapValues { (_, values) ->
              values.maxBy { it.value.length }!!
           }
    

推荐阅读