如何取出部分集合

如何取出部分集合

Kotlin 的標準函式庫中提供了一些函式可以讓你取出部分的集合。

說明

Slice

slice 會回傳索引值的元素值。

val numbers = listOf("A", "B", "C", "D", "E", "F")    
println(numbers.slice(1..3))

索引值會是從 0 開始,結果如下。

[B, C, D]
[one, three, five]
[four, six, one]

你也可以照下面這樣做。

val numbers = listOf("A", "B", "C", "D", "E", "F")
println(numbers.slice(0..4 step 2))
println(numbers.slice(setOf(3, 5, 0))) 

Take and drop

take 顧名思義就是取得某個 index 的元素,因此,你可以使用 take(第n個元素) 來取得該元素值,下面就是取得集合內的第一個元素值。

val numbers = listOf(1, 2, 3, 4, 5, 6)
println(numbers.take(1))

所以得到結果如下。

[1]

你也可以從後面開始數,使用 takeLast(取多少個元素),就是從後面數過來要取幾個元素值。

val numbers = listOf(1, 2, 3, 4, 5, 6)
println(numbers.takeLast(numbers.size))

這樣的結果就會變成如下。

[1, 2, 3, 4, 5, 6]

還是不了解?那取 2 個元素試看看。

val numbers = listOf(1, 2, 3, 4, 5, 6)
println(numbers.takeLast(2))

結果就會如下。

[5, 6]

drop 跟 take 使用方法類似,只是反過來是把某些元素去掉。

val numbers = listOf(1, 2, 3, 4, 5, 6)
println(numbers.drop(1))

這樣結果就會變成去掉第一個元素的集合。

[2, 3, 4, 5, 6]

如果反過來使用 dropLast,就是代表從後面數過來刪除掉第一個元素。

val numbers = listOf(1, 2, 3, 4, 5, 6)
println(numbers.dropLast(1))

那麼結果就會最後一個元素被刪除。

[1, 2, 3, 4, 5]

如果你要從前面開始數就可以使用 takeWhile 並且在 lambda 內設定條件。

可以點進去看原始碼說明。

Returns a list containing first elements satisfying the given [predicate].

從第一個符合條件的元素開始回傳往後的集合。

val numbers = listOf(1, 2, 3, 4, 5, 6)
println(numbers.takeWhile { it < 4 })

所以從前面數過來,取得元素直到 4 < 4 不成立,所以回傳前面 [1, 2, 3],結果如下。

[1, 2, 3]

那如果第一個元素不符合,他就會回傳一個空字串。

val numbers = listOf(1, 2, 3, 4, 5, 6)
println(numbers.takeWhile { it > 4 })

結果如下。

[]

如上面的反過來設定條件,就可以使用 takeLastWhile 並且給定 lambda 一個條件值,直到條件滿足為止。

文件說明如下。

Returns a list containing last elements satisfying the given [predicate].

從最後一個元素符合條件之集合。

val numbers = listOf(1, 2, 3, 4, 5, 6)
println(numbers.takeLastWhile { it < 4 })

因為從後面開始數,第一個結果不符合,就會返回空集合,結果就會如下。

[]

改成第一個條件符合。

val numbers = listOf(1, 2, 3, 4, 5, 6)
println(numbers.takeLastWhile { it == 6 })

結果就會變成只有最後一個元素符合,結果如下。

[6]

當然一樣有 dropWhile 跟 dropLastWhile。

dropWhile 從文件上的描述上可以得知。

Returns a list containing all elements except first elements that satisfy the given [predicate].

從前面第一個元素比對是否符合 lambda 內的條件,不符合的就湊成集合回傳。

val numbers = listOf(1, 2, 3, 4, 5, 6)
println(numbers.dropWhile { it == 1 })

第一個元素會符合所以刪除掉,然後剩下的會回傳,結果如下。

[2, 3, 4, 5, 6]

跟 takeWhile 與 takeLastWhile 相反是 不符合 回傳,因此,如果你第一個條件不符合就會加入回傳集合內。

val numbers = listOf(1, 2, 3, 4, 5, 6)
println(numbers.dropWhile { it > 1 })

結果如下。

[1, 2, 3, 4, 5, 6]

dropLastWhile 則是從集合後方開始數。

val numbers = listOf(1, 2, 3, 4, 5, 6)
println(numbers.dropLastWhile { it == 6 })

結果如下。

[1, 2, 3, 4, 5]

Chunked

chunked 分塊是一種常見的操作,集合中的 chunked() 方法可以幫你把集合切成對應的區塊,根據設定的區塊的大小來區分,直到切滿或者沒切滿形成一個區塊。

val list = (0..12).toList()
println(list.chunked(3))

區分成三部分,最後一步的部分湊不成三塊,因此,就把剩下的一部分弄成一塊,結果如下。

[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12]]

你也可以對分塊後的區塊內進行一些條件設定,比如說將所有的元素進行加總。

val numbers = (0..12).toList() 
println(numbers.chunked(3) { it.sum() })

結果如下。

[3, 12, 21, 30, 12]

Windowed

windowed 可以解釋成滑動窗口,意思有點像是在某個框框內框起來的集合內,所看到的範圍。

val numbers = listOf(1, 2, 3, 4, 5, 6)
println(numbers.windowed(3))

這樣一來就會看到限制 3 個元素的框框,每次平移 1 個元素,因此結果會變成下面結果。

[[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]]

你就會好奇是否可以選擇要平移的步數,當然是可以的。

val numbers = listOf(1, 2, 3, 4, 5, 6)
println(numbers.windowed(3, step = 2, partialWindows = false))

每次平移 2 個單位,所以預期會是 [1, 2, 3] 接著是 [3, 4, 5],那後面沒有辦法移動了怎麼辦?這個就是 partialWindows 這個參數的功能,選擇要不要繼續移動下去,結果如下。

[[1, 2, 3], [3, 4, 5]]

如果我們改成 true 看看。

val numbers = listOf(1, 2, 3, 4, 5, 6)
println(numbers.windowed(3, step = 2, partialWindows = true))

結果就是硬擠。

[[1, 2, 3], [3, 4, 5], [5, 6]]

參考資料

https://kotlinlang.org/docs/reference/collection-parts.html