[Day 26] 集合操作

今天來講一些集合上會用到的好用的函數

集合轉換(Collection Transformations)

顧名思義,集合轉換(Collection Transformations)的意思就是針對集合內的元素,把原本的值轉換成另一種值。

map()

map() 會針對集合內的元素,把原本的值轉換成另一種值。

透過原始碼,可以看到 map() 可以藉由一個 Iterable<T>receiver 來呼叫,map 內可以接收一個 lambda function: transform,最後回傳 List<R>

https://ithelp.ithome.com.tw/upload/images/20201005/20129902MSYPA6SXwd.png

所以這個例子,會把 listOf(1, 2, 3, 4, 5) 每個數字乘以 10 後回傳 List 物件 ,結果會是 [10, 20, 30, 40, 50],也就是透過 map()[1, 2, 3, 4, 5] 轉換成了 [10, 20, 30, 40, 50]

val newList = listOf(1, 2, 3, 4, 5).map { it * 10 }
println(newList) // [10, 20, 30, 40, 50]

由於原始碼宣告是<T,R> 而回傳是 List<R>,代表回傳的型態和輸入可以不一樣

所以也可以是這樣的用法,輸入是數字,回傳是字串。

val newStrList = listOf(1, 2, 3, 4, 5).map<Int, String> { "value $it" }
println(newStrList) // [value 1, value 2, value 3, value 4, value 5]

flatMap()

flatMap() 的用意則是把資料打平的概念,也就是把多個 list 變成一個 list,很像是攤平的意思。

透過原始碼,也可以發現呼叫方式跟 map() 蠻相似的,差別在於 transform 這個 function 是回傳 Iterable

https://ithelp.ithome.com.tw/upload/images/20201005/201299029t82BXfBaO.png

如下面例子,val data 是兩個 List 合成的 List, flatMap {} 內還傳入了 it.map { it * 10} ,it.map {} 會回傳 List 所以會符合原始碼內的 transform: (T) -> Iterable<R> 的定義。

總之,最後把兩個 List 內的值都乘以 10 後,最後合併成一個 list。

val data = listOf(listOf(1, 2, 3, 4, 5), listOf(100, 101, 102, 103, 104))

val flatData2 = data.flatMap { it.map { it * 10 } }
println("data.flatMap *10:$flatData2")
// data.flatMap *10:[10, 20, 30, 40, 50, 1000, 1010, 1020, 1030, 1040]

flatten()

不過其實如果只是要合併 List 成一個,不做其他動作的話,使用 flatten() 就可以了,下面的例子就只是單純把兩個 List 合併成一個。

val data = listOf(listOf(1, 2, 3, 4, 5), listOf(100, 101, 102, 103, 104))
val flatData = data.flatten()
println("data.flatten$flatData")
// data.flatten[1, 2, 3, 4, 5, 100, 101, 102, 103, 104]

過濾(Filtering)

filter()

filter() 接受一個 prdicate function ,這個 function 回傳值是 Boolean,也就是當滿足過濾的條件時,資料會被留下來

https://ithelp.ithome.com.tw/upload/images/20201005/20129902DfAa48cZ4u.png

所以像這個例子,[1, 2, 3, 4, 5, 6] 滿足 % 2 為 0 的話,就會被保留下來,也就是 [2, 4, 6]

val filterResult = listOf(1, 2, 3, 4, 5, 6).filter { it % 2 == 0 }
println("filterResult:$filterResult")
// filterResult:[2, 4, 6]

測試 predicate

針對一些 predicate function,還有這些方法可以使用

any() - 任一元素滿足條件,回傳 true

none() - 沒有任何元素滿足條件,回傳 true

all() - 全部元素滿足條件,回傳 true

所以針對下面的例子,val numbers = listOf("one", "two", "three", "four")

numbers.any { it.endsWith("e") } 因為有元素有結尾是 e 這個字,所以是 true

numbers.none { it.endsWith("a") } 沒有任何元素有結尾是 a,所以是 true

numbers.all { it.endsWith("e") } 全部元素並不是都是結尾是 e,所以是 false

val numbers = listOf("one", "two", "three", "four")
println(numbers.any { it.endsWith("e") }) // true
println(numbers.none { it.endsWith("a") }) // true
println(numbers.all { it.endsWith("e") }) // false

尋找質數

這裡有一個很棒的例子,展現了目前提到的方法的威力

首先,搞清楚質數的定義

質數:只能被 1 或 本身除盡的數,以 7 為例子,7 只能被 1 或 7 除盡,所以 7 是 質數

所以這裡其實 filter 的內容就是,以 7 為例子,就是跑 2~6,每個去做 7%2, 7%3, 7%4, 7%5, 7%6 看每個是不是都不能被除盡,那就回傳 true,代表是質數。

// 所以如果 7 % (2~6) == 0 => 不是質數
val nums = listOf(7, 4, 8, 4, 3)
val primes = nums.filter { num ->
    (2 until num).map {
        num % it
    }.none { it == 0 } // Returns `true` if no elements match the given [predicate].
}
println("質數: $primes") // 質數: [7, 3]

如果對 until 不熟悉,可以查看官網的例子,很簡單 https://kotlinlang.org/docs/reference/ranges.html

partition()

partition() 可以把集合,針對 predicate 的 true false,分成兩群

val partitions = listOf(1, 2, 3, 4, 5, 6).partition { it % 2 == 0 }
println("partitions: $partitions")
//partitions: ([2, 4, 6], [1, 3, 5])

合併

zip()

zip 會按照位置把兩個集合,合併成新的集合,新的集合長度會是原本兩個集合中比較短的那一個。

val z1 = listOf(1, 2, 3, 4, 5, 6)
val z2 = listOf(10, 20, 30)
val zipResult: List<Pair<Int, Int>> = z1.zip(z2)
println("zipResult: $zipResult")
//zipResult: [(1, 10), (2, 20), (3, 30)]

reduce()

val reducedValue = listOf(1, 2, 3, 4).reduce { acc, num ->
    println("reduced value: acc:$acc, num:$num")
    acc + num
}
println("reducedValue final result: $reducedValue \n\n")
/*
    reduced value: acc:1, num:2
    reduced value: acc:3, num:3
    reduced value: acc:6, num:4
    reducedValue final result: 10 
 */

reduceRight()

val reducedValueRight = listOf(1, 2, 3, 4).reduceRight { num, acc ->
    println("reduced value from Right: acc:$acc, num:$num")
    acc + num
}
println("reducedValue from Right final result: $reducedValueRight \n\n")
/*
    reduced value from Right: acc:4, num:3
    reduced value from Right: acc:7, num:2
    reduced value from Right: acc:9, num:1
    reducedValue from Right final result: 10
 */

fold()

val foldedValue = listOf(1, 2, 3, 4).fold(100) { acc, num ->
    println("accumulated value: acc:$acc, num:$num")
    acc + num
}
println("foldedValue final result: $foldedValue \n\n")
/*
    accumulated value: acc:100, num:1
    accumulated value: acc:101, num:2
    accumulated value: acc:103, num:3
    accumulated value: acc:106, num:4
    foldedValue final result: 110
 */

foldRight()

val foldedValueRight = listOf(1, 2, 3, 4).foldRight(100) { num, acc ->
    println("accumulated value from Right: acc:$acc, num:$num")
    acc + num
}
println("foldedValue final from Right result: $foldedValueRight \n\n")
/*
    accumulated value from Right: acc:100, num:4
    accumulated value from Right: acc:104, num:3
    accumulated value from Right: acc:107, num:2
    accumulated value from Right: acc:109, num:1
    foldedValue final from Right result: 110
 */

以上就是今天的內容!謝謝大家!

今日練習的程式在這: 請點我