Kotlin Programming 2022 Spring - Lecture 6
===
###### tags: `Kotlin 2022 Spring` `Kotlin` `2022 Spring`
This lecture intends to cover the materials in Chapter 10 and Chapter 11. However, the lecturer encourage you to read the [official documents about collections](https://kotlinlang.org/docs/reference/collections-overview.html).
## List
+ Ordered collection of elements
+ Repeated values are allowed
+ Elements have the same type
+ `List<Int>`
+ `List<Double>`
+ `List<String>`
+ `String.split()` returns `List<String>`
+ `List<Any>`
+ `List<List<Int>>`
```kotlin
fun main() {
val listInt = listOf(1, 2, 3)
val listDouble = listOf(1.0, 2.0, 3.0)
val listString = listOf("a", "bc", "xyz")
val listAny = listOf(1, 2.0, "3")
val listListInt = listOf(listOf(1, 2), listOf(3, 4))
println(listInt)
println(listDouble)
println(listString)
println(listAny)
println(listListInt)
}
```
+ Indexed by integer: zero based
+ `.size` is the number of elements
+ Use `.get(i)` or `[i]` to index
+ [`.getOrNull(i)`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/get-or-null.html) returns `null` if `i` is invalid.
+ [`.getOrElse(i){expression}`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/get-or-else.html) returns `expression` if `i` is invalid.
+ Use `.last()` to access the last element
+ `.indexOf()` and `.lastIndexOf()`
```kotlin
fun main() {
val listInt = listOf(1, 2, 3, 2, 3, 2, 5)
println("$listInt has size ${listInt.size}")
println("listInt.get(0) is ${listInt.get(0)}")
println("listInt[1] is ${listInt[1]}")
println("listInt.last() is ${listInt.last()}")
println("The first index of 2 is ${listInt.indexOf(2)}")
println("The last index of 2 is ${listInt.lastIndexOf(2)}")
// should crash
println("listInt[listInt.size] is ${listInt[listInt.size]}")
// should be null
println("listInt.getOrNull(listInt.size) is ${listInt.getOrNull(listInt.size)}")
// should be listInt.size * listInt.size
println("listInt.getOrElse(listInt.size){it * it} is ${listInt.getOrElse(listInt.size){it * it}}")
}
```
+ Create a list
+ `listOf`
+ `List(size){expression}` -- [reference](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list.html)
```kotlin
fun main() {
val numbers = listOf(1,2,3,4,5,6,7,8,9,10)
println(numbers)
val reversedNumbers = List(10){10 - it}
println(reversedNumbers)
}
```
+ `for`-loop
+ `.withIndex()` -- [reference](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/with-index.html)
```kotlin
fun main() {
val listInt = listOf(1, 2, 3)
println("listInt contains:")
for (element in listInt) {
println(element)
}
println("If you need indexes, use .withIndex().")
for ((index,element) in listInt.withIndex()) {
println("listInt[$index] is $element")
}
}
```
+ `contains` and `containsAll`
```kotlin
fun main() {
val listInt = listOf(1, 2, 3, 3)
val x = 3
if (listInt.contains(x)) println("$listInt contains $x")
val y = 5
if (listInt.contains(y)) println("$listInt contains $y")
val z = listOf(1,2,2,3)
if (listInt.containsAll(z)) println("$listInt contains all $z")
}
```
+ Transform, filter, aggregate
```kotlin
fun main() {
val toks = "1 3 1 224 113".split(" ")
println("toks is $toks")
println("toks.first()+toks.last() is ${toks.first()+toks.last()}")
val ints = toks.map{it.toInt()}
println("ints is $ints")
println("ints.first()+ints.last() is ${ints.first()+ints.last()}")
val oddInts = ints.filter{it % 2 != 0}
println("oddInts is $oddInts")
println("oddInts.sum() is ${oddInts.sum()}")
println("oddInts.max() is ${oddInts.max()}")
println("oddInts.min() is ${oddInts.min()}")
val prod = oddInts.reduce{x, y -> x * y}
println("The product of $oddInts is $prod")
}
```
+ [`any`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/any.html) and [`all`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/all.html)
```kotlin
fun main() {
val listInt = listOf(1, 2, 3, 3)
if (listInt.any{it % 2 == 0})
println("$listInt contains an even number.")
if (listInt.all{it % 2 == 0})
println("Elements in $listInt are all even.")
val x = 3
if (listInt.any{it == x})
println("$listInt contains $x.")
val y = listOf(1, 2, 2, 3)
if (y.all{listInt.contains(it)})
println("$listInt contains all elements in $y.")
}
```
+ Mutable version: `MutableList` -- [reference](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-list/)
+ `mutableListOf`
+ `.toMutableList` and `.toList`
+ `add`
+ `removeAt` vs `remove`
+ `set` vs index
```kotlin
fun main() {
val mList = mutableListOf(1, 2, 3, 2, 3, 2, 5)
mList.add(-9)
println(mList)
mList.add(5,566)
println(mList)
mList.removeAt(4)
println(mList)
mList.remove(2)
println(mList)
mList.set(2, 7)
// mList[2] = 7
println(mList)
}
```
+ Shuffle and sort
```kotlin
fun main() {
val mList = mutableListOf(1, 2, 3, 2, 7, 2, 5, -9)
mList.shuffle()
println(mList)
mList.sort()
println(mList)
mList.sortBy{-it}
println(mList)
mList.sortWith(Comparator{x, y -> y * y - x * x})
println(mList)
}
```
+ Mutable vs immutable
```kotlin
fun main() {
var immutable = listOf(1, 2, 3)
println(immutable)
// You cannot reassign any element of a List
// immutable[0] = 5
// But you can reassign the variable immutable
immutable = listOf(5, 2, 3)
println(immutable)
val mutable = mutableListOf(1, 2, 3)
println(mutable)
// You can reassign any element of a MutableList
mutable[0] = 5
// You cannot reassign the value mutable
// mutable = mutableListOf(5, 2, 3)
println(mutable)
}
```
### Practice
#### Task 1
Help your instructor to adjust the scores. Transform the original score $x$ to $\left\lceil 10*\sqrt{x}\right\rceil$.
+ Input format: The input has only one line, and there are several original scores separated by spaces. Between any two adjacent scores, there is exactly one space.
+ Output format: Output the adjusted scores in the same format.
+ Sample input:
```
49 70 90
```
+ Sample output:
```
70 84 95
```
#### Task 2:
Sort words into the reversed alphabetical order while ignoring the cases. That is, both `Banana` and `cAT` should appear before `aPpLe`.
+ There are several words separated by spaces, and there is exactly one space between adjacent words.
+ All words are different.
+ Sample input:
```
ApPlE cAT Banana
```
+ Sample output:
```
cAT Banana ApPlE
```
#### Task 3
Read an arithmetic expression and find the index of the last evaluated operator.
+ Operators includes `+`, `-`, `*`, `/`
+ The brackets `(` and `)` are properly balanced.
+ All operands are non-negative decimal numbers.
+ No two numbers are adjacent.
+ You may assume that there is always a space between any pair of operators, between an operator and an operand, and between a bracket and any operator/operand.
+ Valid inputs:
+ `( 1 + 2 ) * 3 * ( 4 + 5 )`
+ Invalid inputs:
+ `1 2 + 3`
+ `1` and `2` cannot be adjacent
+ `(1+2)*3`
+ Lack of proper spaces
+ `1++2`
+ Not an expression
+ `2(3)`
+ Not an expression
+ You may output anything if the expression is invalid.
+ Sample input
```
( 1 + 2 ) * 3 * ( 4 + 5 )
```
+ Sample output
```
14
```
## Set
+ Collection of unique elements
+ No repeated values
+ Elements have the same type
+ `Set<Int>`
+ `Set<Double>`
+ `Set<String>`
+ The order of elements has no significance in general.
+ But the default implementation `LinkedHashSet` stores the insertion order of elements.
+ Much faster `.contains()`
```kotlin
fun main() {
val numbers = setOf(1, 2, 3)
val revNumbers = setOf(3, 2, 1)
val mutableNumbers = mutableSetOf(2, 1, 3)
println("numbers: $numbers")
println("revNumbers: $revNumbers")
println("mutableNumbers: $mutableNumbers")
println("Are numbers and revNumbers equal? ${numbers == revNumbers}")
println("Are numbers and mutableNumbers equal? ${numbers == mutableNumbers}")
}
```
### Faster `.contains` and `in` operator
```kotlin
fun main() {
val numbers = setOf(1, 2, 3)
println("numbers: $numbers")
println("numbers contains 5: ${numbers.contains(5)}")
println("numbers contains 5: ${5 in numbers}")
println("numbers does not contain 5: ${!numbers.contains(5)}")
println("numbers does not contain 5: ${5 !in numbers}")
}
```
### Add and subtract
We can change a `MutableSet` by adding element to it and removing elements from it.
+ Adding: use `+` or `+=`
+ `+` does not change the `MutableSet` but create a new instance of `Set` or `MutableSet`.
+ `+=` changes the `MutableSet`
+ Removing: use `-` or `-=`
```kotlin
fun main() {
val numbers = mutableSetOf(1, 2, 3)
val ref = numbers
// Use + will not change numbers, but create a new instance
(numbers + 4).also { println("numbers + 4 = $it: ${it is MutableSet<Int>}") }
println(numbers)
// Use += will change numbers
numbers += 4
println(numbers)
println(ref)
// Note: numbers is a val not a var.
// numbers = numbers + 5
// println(numbers)
numbers -= 2
println(numbers)
numbers += listOf(5, 6)
println(numbers)
numbers -= setOf(4, 3, 9)
println(numbers)
}
```
#### `union` and `intersect`
Both of them are infix function
```kotlin
fun main() {
val numbers = setOf(1, 2, 3, 4, 5)
val evenNumbers = setOf(0, 2, 4, 6, 8)
println(numbers union evenNumbers)
println(numbers.union(evenNumbers))
println(numbers intersect evenNumbers)
println(numbers.intersect(evenNumbers))
println("numbers: $numbers")
println("evenNumbers: $evenNumbers")
}
```
If you want to change the `MutableSet`, please consider the following usage.
```kotlin
fun main() {
val numbers = mutableSetOf(1, 2, 3, 4, 5)
// union and change
numbers += setOf(0,2,4,6,8)
println(numbers)
// intersect and change
numbers.removeIf{it !in setOf(1,3,5,7,9)}
println(numbers)
}
```
## Map
+ A set of key-value pairs
+ Keys are unique
+ `.keys`
+ Values can be repeated
+ `.values`
+ Can be considered as a more general List
+ Values are indexed by key
```kotlin
fun main() {
val numMap = mapOf("one" to 1, "two" to 2, "zero" to 0)
println("All keys: ${numMap.keys}")
println("All values: ${numMap.values}")
// in operator checks key, not key-value pair.
// Equivalent to .contains(key)
if ("one" in numMap)
println("Value by key \"one\": ${numMap["one"]}")
if (1 in numMap.values)
println("The value 1 is in the map")
// .containsValue works like .values.contains
if (numMap.containsValue(1))
println("The value 1 is in the map")
// different to in operator
// for-loop iterates key-value pairs
for (item in numMap)
println("$item has type ${item::class.qualifiedName}")
for ((key, value) in numMap)
println("key: $key, value: $value.")
}
```
+ Equality: Check if all key-value pairs are equal regardless their order.
```kotlin
fun main() {
val numMap = mapOf("one" to 1, "two" to 2, "zero" to 0)
val another = mapOf("zero" to 0, "one" to 1, "two" to 2)
println("numMap: $numMap")
println("another: $another")
println("numMap == another: ${numMap == another}")
println("string representations are equal: ${"$numMap" == "$another"}")
}
```
### Get methods
+ `.get(key)` and `[key]` return `null` if `.keys` does not contain `key`.
+ Similar to `List.getOrNull`
+ `.getValue(key)` throws an `Exception` if `.keys` does not contain `key`.
+ Similar to `List.get`
+ `getOrElse(key){expression}` returns the value of expression if `.keys` does not contain `key`.
+ `getOrDefault(key,defaultValue)` returns `defaultValue` if `.keys` does not contain `key`.
```kotlin
fun main() {
val numMap = mapOf("one" to 1, "two" to 2, "zero" to 0)
println("numMap.get(\"one\"): ${numMap.get("one")}")
println("numMap[\"one\"]: ${numMap["one"]}")
println("numMap.getValue(\"one\"): ${numMap.getValue("one")}")
println("numMap.get(\"TWO\"): ${numMap.get("TWO")}")
println("numMap[\"TWO\"]: ${numMap["TWO"]}")
// CRASH!!
// println("numMap.getValue(\"TWO\"): ${numMap.getValue("TWO")}")
println("numMap.getOrElse(\"TWO\"){222}: ${numMap.getOrElse("TWO") { 222 }}")
println("numMap.getOrDefault(\"TWO\",333): ${numMap.getOrDefault("TWO", 333)}")
// It is also ok to use ?: operator
println("numMap[\"TWO\"]?:444 : ${numMap["TWO"] ?: 444}")
}
```
### `MutableMap`
#### Put Methods
+ `.put(key, value)`
+ `[key] = value`
+ `.getOrPut(key,value)`
+ `.putAll(pairs)`
```kotlin
fun main() {
val map = mutableMapOf<String, Int>()
map.put("zero", 0)
println(map)
map["one"] = 0
println(map)
map["one"] = 1
println(map)
// getOrPut is get if the key is in the map
println(map["one"])
println(map.getOrPut("one") { 2 })
println(map["one"])
// getOrPut is put if the key is not in the map
println(map["two"])
println(map.getOrPut("two") { 2 })
println(map["two"])
// putAll changes the existed key-value pairs
map.putAll(mapOf("two" to -2, "three" to 3))
println(map)
// putAll accepts list of pairs
map.putAll(listOf(Pair("four", 4), Pair("five", 5)))
println(map)
// to is the infix version of Pair
map.putAll(listOf("Six" to 6, "Ten" to 10))
println(map)
}
```
#### Remove methods
+ `remove(key)`
+ Returns `null` if the `MutableMap` does not contain `key`.
+ Otherwise, returns the corresponding value.
+ `remove(key,value)` return `Boolean`
+ If `.get(key) == value`, remove the key-value pair and returns true.
+ Otherwise returns false.
```kotlin
fun main() {
val map = mutableMapOf("a" to 1, "b" to 2, "c" to 3, "d" to 4)
// remove a key in the map
println(map.remove("a"))
println(map)
// remove a key not in the map
println(map.remove("z"))
println(map)
// remove a key-value pair in the map
println(map.remove("b", 2))
println(map)
// remove a key-value pair not in the map
println(map.remove("c", -1))
println(map)
println(map.remove("D", 4))
println(map)
}
```
#### `plus` and `minus` Operation
+ Works for both immutable and mutable maps.
+ Create new `Map` instances.
+ A map `+` an element: create a new `Map` that contains all elements in the map and the element.
+ `+` on two maps: create a new `Map` that contains all elements in both sides.
+ A map `-` a key: create a new `Map` that contains all elements in the map but the element.
+ A map `-` a list of keys: create a new `Map` that contains the elements in the left hand side but the keys in the right hand side.
```kotlin
fun main() {
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
println(map + Pair("d", 4))
println(map + Pair("e", 5))
println(map + ("z" to 26))
println(map + mapOf("f" to 6, "g" to 7))
println(map + listOf("c" to -3, "y" to 25))
// map remains unchanged
println(map)
println(map - "a")
println(map - "b")
println(map - listOf("a", "c"))
println(map - listOf("b", "d"))
// map remains unchanged
println(map)
}
```
#### `plusAssign` and `minusAssign` Operatons
+ `+=` a key-value pair: `.put(key,value)`
+ `+=` a collection of key-value pairs: `.putAll(pairs)`
+ `-=` a key: `.remove(key)`
+ `-=` a collection of keys: `for (key in keys)` + `.remove(key)`
```kotlin
fun main() {
val map = mutableMapOf<String, Int>()
map += "a" to 1
println(map)
map += Pair("b", 2)
println(map)
map += mapOf("c" to 3, "d" to 4)
println(map)
map += listOf("e" to 5, "z" to 26)
println(map)
map -= "a"
println(map)
map -= "A"
println(map)
map -= listOf("e", "f", "z")
println(map)
}
```
#### Iterating Items and Destructuring
+ Iterate over map: Entry
+ `.key`
+ `.value`
+ `.filterKeys`, `.mapKeys`, ...
+ `.filterValues`, `.mapValues`, ...
```kotlin
fun main() {
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
// Entry have .key and .value
println(map.filter { it.key < "b" || it.value > 2 })
// Destructuring Entry into (key, value)
println(map.filter { (key, value) -> key < "b" || value > 2 })
// filter with key
println(map.filterKeys { it < "b" })
// filter with value
println(map.filterValues { it > 2 })
}
```
### Practice for `Map`
#### Task 1
Write a function `fun mostFrequent(str: String): Char?`
+ It returns `null` if `str` is `""`.
+ Otherwise, it returns the `Char` that has most occurences in `str`. If there are multiple candidates, output the smallest one in alphabetic order.
#### Task 2
Write a function `fun wordCount(str: String): Map<String,Int>`
+ It returns a `Map`.
+ `str` contains only spaces and English alphabets.
+ Keys are words in lowercases.
+ Values are the number of occurences of the correspoinding keys in `str`.
#### Task 3
Write a function `fun printMap(map: Map<String,Int>): Unit` that print the key-value pairs of `map` in dictionary order (case insentitive).