iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 14
0

過濾(Filtering)是 Collection 操作裡很常用的動作,這個功能一如前面幾個章節裡的範例是以 Lambda 來實作,要注意的是,這個實作並不會更動原本的 Collection,所以在使用過濾的功能時,記得要將結果用一個新的變數來儲存。

過濾

要過濾 Collection 裡的資料,只要傳入一個有條件判斷的 Lambda,Collection 就會把所有元素一個個用 Lambda 做驗證,只要回傳 true 的就會留下,反之則被過濾掉。各 Collection 間稍為有點不同的是,ListSetfilter() 後都會回傳 List,而 Map 則是回傳 Map

val numbers = listOf("one", "two", "three", "four")  
val longerThan3 = numbers.filter { it.length > 3 } // [three, four]

val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
val filteredMap = numbersMap.filter { (key, value) -> key.endsWith("1") && value > 10} // {key11=11}

val originalMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3)
val filteredMap = originalMap.filter { it.value < 2 } // {key1=1}

假如你需要過濾的 Lambda 是反向來符合語意的話,Collection 也有一個 filterNot{},可以使用,只要 Lambda 回傳 true 的就會濾掉,反之則被留下。

val numbers = listOf("one", "two", "three", "four")
val filteredNot = numbers.filterNot { it.length <= 3 } // [three, four]

假如在過濾的邏輯裡,index 對你來說是重要的,那可以用 filterIndexed{},Collection 會把 index 及 element 傳進 Lambda 裡,你可以用這些參數來做條件判斷。

val numbers = listOf("one", "two", "three", "four")
val filterIndexed = numbers.filterIndexed { index, s -> (index != 0) && (s.length < 5)  } // [two, four]

過濾 Type

假如當初這個 Collection 的彈性設計的很大,比方說是一個 List<Any>,則這個 List 裡就有可能有各種 Type 的元素。假如我們想要過濾出指定 Type 的元素的話,可以用 filterIsInstance<T>()

val numbers = listOf(null, 1, "two", 3.0, "four")
val filterIsString = numbers.filterIsInstance<String>() // [two, four]

過濾 Null

若你的 Collection 是 List<T?>,也就是說元素有可能是 null,則可以用 filterNotNull() 快速地把所有 null 過濾掉,回傳的就是 List<T: Any>,方便處理空安全。

val numbers = listOf(null, "one", "two", null)
val filterNotNull = numbers.filterNotNull() // [one, two]

過濾重覆

Array 和 List 內的元素是可以重覆的,假如需要使用這兩種 Collection,但一時需要把重覆的元素過濾掉呢?這時可以用 distinct(),會自動去除掉 Collection 裡重覆的元素成一個新的 List。

val array = arrayOf(1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 5, 6, 7, 8, 9)
val uniqueArray = array.distinct() // [1, 2, 3, 4, 5, 6, 7, 8, 9]

val list = listOf("one", "one", "two", "one", "three")
val uniqueList = list.distinct() // [one, two, three]

假如過濾重覆的邏輯比較複雜想要自行定義的話,則可以用 distinctBy{},一樣透過傳入 Lambda 的方式來過濾元素即可。

data class SmallClass(val key: String, val num: Int)

val listOfSmallClass = listOf(
    SmallClass("key1", 1),
    SmallClass("key2", 3),
    SmallClass("key3", 3),
    SmallClass("key4", 4),
)
val distinctList = listOfSmallClass.distinctBy { it.num } // [SmallClass(key=key1, num=1), SmallClass(key=key2, num=3), SmallClass(key=key4, num=4)]

切割

一種比較特別的情境,你想用 Lambda 過濾出符合條件與不符合條件的兩個清單,也就是說想要拿到一個包含兩個 ListPair,這時我們可以用 partition()。其回傳值會是一個 Pair,一個清單是符合 Lambda 條件的所有元素,另一個清單是所有沒通過的元素,我們可以直接用兩個變數把 List 接起來。

val numbers = listOf("one", "two", "three", "four")
val (match, rest) = numbers.partition { it.length > 3 }

// match 是 [three, four]
// rest 是 [one, two]

表格整理

在這個章節裡,我們討論許多過濾 Collection 的方式,學會這些動作後,對於操作 Collection 會更上手!為了一覽這些 API 在不同 Collection 上的行為,以下用表格來整理本章所討論到的 method:

行為 Array List Set Map
filter{} 依 Lambda 過濾 v v v v
filterNot{} 依 Lambda 反向過濾 v v v v
filterIndexed{} Lambda 過濾含 index v v v x
filterIsInstance() 過濾出相同型別的元素 v v v x
filterNotNull() 過濾掉 null 的元素 v v v x
distinct() 過濾重覆的元素 v v v x
distinctBy{} 依 Lambda 過濾重覆 v v v x
partition{} 依 Lambda 回傳 Pair v v v x

參考資料


上一篇
第十三天:Collection 操作之截取
下一篇
第十五天:Collection 操作之排序
系列文
新手也能懂的 Kotlin Collection 賞玩門道31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言