【问题标题】:Create list from two uneven lists从两个不均匀列表创建列表
【发布时间】:2022-06-10 21:03:06
【问题描述】:

我是 Kotlin 的新手,它很复杂,但有两个未知大小和内容的列表可能看起来像这样

codes = ["or", "or", "or", "parks", "parks", "wa", "wa", "wa", "id"]
types = ["STATE", "NATIONAL", "STATE", "STATE"]

每个typecodes 中的一个非独特项相关(例如parks->NATIONALwa->STATE),但需要STATEs 的总数.在这种情况下,预计会有 7 个STATEs i=。

我最初的想法是做这样的事情

var typesIdx = 0
var prevCode = ""

val totalList = mutableListOf<String>()
    
for (currCode in codes) {
    if (currCode != prevCode) {
        prevCode = currCode
        typesIdx+=1
    }    
    totalList += types.get(typesIdx).toString()
} 

但我觉得有一种更好、更智能的方法来实现这一点,它实现了更多 Kotlin 的内置函数,而不是简单地循环和逐位创建列表

【问题讨论】:

  • 不同代码的数量是否等于types的大小?

标签: android kotlin


【解决方案1】:

如果我已经正确理解了您想要做什么,您可以使用列表中的distinct() 方法在此处提供帮助。它返回一个仅包含与原始列表不同的元素的列表,并保留出现的顺序。

val codes = listOf("or", "or", "or", "parks", "parks", "wa", "wa", "wa", "id")
val types = listOf("STATE", "NATIONAL", "STATE", "STATE")

// First, condense the "codes" list down to its distinct entries - which
// should make it the same size as "Types"
val condensedCodes = codes.distinct()
println(condensedCodes) // ["or","parks","wa","id"]

// Then create a map from code to type
val typeMap = condensedCodes.zip(types).toMap()
println(typeMap) // {or=STATE, parks=NATIONAL, wa=STATE, id=STATE}

// Then use that map to count the original codes list based on type
val numStates = codes.count { typeMap[it] == "STATE" }
println(numStates) // prints 7

// or if you want the list of states
val states = codes.filter { typeMap[it] == "STATE" }
println(states) // [or, or, or, wa, wa, wa, id]

// or if you want to transform the codes list to a list of types
val typeOfCodes = codes.map { typeMap[it] }
println(typeOfCodes) // [STATE, STATE, STATE, NATIONAL, NATIONAL, STATE, STATE, STATE, STATE]

如果同一组代码出现在列表中的多个位置,上述方法将不起作用。您不能再使用distinct,但仍然可以使用以下方法:

val codes = listOf("or", "or", "or", "parks", "parks", "wa", "wa", "id", "or", "or")
val types = listOf("STATE", "NATIONAL", "STATE", "STATE", "STATE")

val condensedCodes = codes.zipWithNext()
                          .filter { it.first != it.second }
                          .map { it.first } + codes.last()

这是如何工作的? zipWithNext() 创建一个这样的列表

[(or, or), (or, or), (or, parks), ...

然后它会被过滤到仅来自不匹配对的第一个元素,本质上是选择每组重复的最后一个元素。这样就漏掉了最后一组,所以我们在最后加上codes.last()

["or", "or", "or", "parks", "parks", "wa", "wa", "wa", "id"]
              ^              ^                    ^          
[            "or",          "parks",             "wa"      ] + "id"

如果您要在很多地方使用它,您可以为列表定义一个 extension function(Kotlin 的一个简洁功能)

fun <T> List<T>.condense() = when(isEmpty()) {
    true -> listOf()
    else -> zipWithNext().filter { it.first != it.second }.map { it.first } + last()
}

让你随便用

val condensedCodes = codes.condense()

【讨论】:

    【解决方案2】:

    如果我对您的问题的理解是正确的,您只想将您的 for 循环替换为 kotlin 标准库函数并获得 totalList 的输出。

    你可以这样做

    val totalList = codes
        .groupBy { it } // Create a map -> {or=[or, or, or], parks=[parks, parks], wa=[wa, wa, wa], id=[id]}
        .values // Get a list of values from the previous map -> [[or, or, or], [parks, parks], [wa, wa, wa], [id]]
        .flatMapIndexed { index, v -> // flatten inner and outer lists while transforming each inner list
            v.map { types[index] } // In inner lists, replace [codes] element with [types] element
        }
    

    总之

    val totalList = codes.groupBy { it }.values.flatMapIndexed { index, v -> v.map { types[index] } }
    

    最终输出是

    [STATE, STATE, STATE, NATIONAL, NATIONAL, STATE, STATE, STATE, STATE]
    

    注意点:由于重复迭代以及分配更多集合,这比您的原始代码性能低。

    【讨论】:

      【解决方案3】:
      val codes = listOf("or", "or", "or", "parks", "parks", "wa", "wa", "wa", "id")
      val types = listOf("STATE", "NATIONAL", "STATE", "STATE")
      
      val result = codes
        .filterIndexed { index, i -> if (index > 0) i != codes[index - 1] else true }
        .zip(types)
        .filter { (_, type) -> type == "STATE" }
        .map { (code, _) -> code }
        .let { list -> codes.count { code -> code in list }  }
        
      println(result)   // Output: 7
      

      步骤:

      filterIndexed: 
                [or, parks, wa, id]
      zip:      [(or, STATE), (parks, NATIONAL), (wa, STATE), (id, STATE)]
      filter:   [(or, STATE), (wa, STATE), (id, STATE)]
      map:      [or, wa, id]
      let:      counts how often the elements in [or, wa, id] appear in 'codes'
      

      请注意,带有 filterIndexed 的行将产生不同的连续值。 distinct 不会像@TylerV 在他的回答中正确陈述的那样工作:

      "or", "wa", "or", "parks", "parks", "wa", "wa", "wa", "id"
      ... would become
      with distinct():        "or", "wa", "parks", "id"
      with filterIndex {...}: "or", "wa", "or", "parks", "wa", "id"
      

      【讨论】:

        猜你喜欢
        • 2017-10-03
        • 2015-09-21
        • 1970-01-01
        • 2014-06-29
        • 2021-11-12
        • 2016-10-18
        • 1970-01-01
        • 1970-01-01
        • 2022-12-08
        相关资源
        最近更新 更多