Slide 1

Slide 1 text

Idiomatic Kotlin

Slide 2

Slide 2 text

@antonarhipov

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

Idiomatic - using, containing, or denoting expressions that are natural to a native speaker

Slide 7

Slide 7 text

Idiomatic - using, containing, or denoting expressions that are natural to a native speaker Commonly accepted style

Slide 8

Slide 8 text

Idiomatic - using, containing, or denoting expressions that are natural to a native speaker Commonly accepted style E ff ective use of language features

Slide 9

Slide 9 text

Top-Level (Extension) Functions

Slide 10

Slide 10 text

fun main() { doSomething() } fun doSomething() { doMoreStuff( : : finishWork) } fun doMoreStuff(callback: () -> Unit) { callback() } fun finishWork() { TODO("Not implemented yet") }

Slide 11

Slide 11 text

fun main() { doSomething() } fun doSomething() { doMoreStuff( : : finishWork) } fun doMoreStuff(callback: () -> Unit) { callback() } fun finishWork() { TODO("Not implemented yet") }

Slide 12

Slide 12 text

fun main() { doSomething() } fun doSomething() { doMoreStuff( : : finishWork) } fun doMoreStuff(callback: () -> Unit) { callback() } fun finishWork() { TODO("Not implemented yet") }

Slide 13

Slide 13 text

fun main() { doSomething() } fun doSomething() { doMoreStuff( : : finishWork) } fun doMoreStuff(callback: () -> Unit) { callback() } fun finishWork() { TODO("Not implemented yet") } Just functions, no classes!

Slide 14

Slide 14 text

Don’t create classes just to hold functions!

Slide 15

Slide 15 text

class StringUtils { companion object { fun isPhoneNumber(s: String) = s.length = = 7 & & s.all { it.isDigit() } } }

Slide 16

Slide 16 text

class StringUtils { companion object { fun isPhoneNumber(s: String) = s.length = = 7 & & s.all { it.isDigit() } } } object StringUtils { fun isPhoneNumber(s: String) = s.length = = 7 & & s.all { it.isDigit() } }

Slide 17

Slide 17 text

class StringUtils { companion object { fun isPhoneNumber(s: String) = s.length = = 7 & & s.all { it.isDigit() } } } object StringUtils { fun isPhoneNumber(s: String) = s.length = = 7 & & s.all { it.isDigit() } } fun isPhoneNumber(s: String) = s.length == 7 && s.all { it.isDigit() }

Slide 18

Slide 18 text

class StringUtils { companion object { fun isPhoneNumber(s: String) = s.length = = 7 & & s.all { it.isDigit() } } } object StringUtils { fun isPhoneNumber(s: String) = s.length = = 7 & & s.all { it.isDigit() } } fun isPhoneNumber(s: String) = s.length == 7 && s.all { it.isDigit() } fun String.isPhoneNumber() = length == 7 && all { it.isDigit() }

Slide 19

Slide 19 text

Extension or a member? https://kotlinlang.org/docs/coding-conventions.html#extension-functions Use extension functions liberally Restrict the visibility to minimize API pollution As necessary, use local extension functions, member extension functions, or top-level extension functions with private visibility

Slide 20

Slide 20 text

+

Slide 21

Slide 21 text

fun findMessageById(id: String) = db.query( "select * from messages where id = ?", RowMapper { rs, _ -> Message(rs.getString("id"), rs.getString("text")) }, id ) val db: JdbcTemplate = ...

Slide 22

Slide 22 text

fun findMessageById(id: String) = db.query( "select * from messages where id = ?", RowMapper { rs, _ -> Message(rs.getString("id"), rs.getString("text")) }, id ) @Override public List query(String sql, RowMapper rowMapper, @Nullable Object . .. args) throws DataAccessException { return result(query(sql, args, new RowMapperResultSetExtractor < > (rowMapper))); }

Slide 23

Slide 23 text

fun findMessageById(id: String) = db.query( "select * from messages where id = ?", RowMapper { rs, _ -> Message(rs.getString("id"), rs.getString("text")) }, id )

Slide 24

Slide 24 text

fun findMessageById(id: String) = db.query( "select * from messages where id = ?", RowMapper { rs, _ -> Message(rs.getString("id"), rs.getString("text")) }, id )

Slide 25

Slide 25 text

fun findMessageById(id: String) = db.query( "select * from messages where id = ?", id, RowMapper { rs, _ -> Message(rs.getString("id"), rs.getString("text")) } ) vararg

Slide 26

Slide 26 text

fun findMessageById(id: String) = db.query( "select * from messages where id = ?", id, { rs, _ -> Message(rs.getString("id"), rs.getString("text")) } ) SAM conversion

Slide 27

Slide 27 text

fun findMessageById(id: String) = db.query( "select * from messages where id = ?", id) { rs, _ -> Message(rs.getString("id"), rs.getString("text")) } Trailing lambda parameter

Slide 28

Slide 28 text

fun findMessageById(id: String) = db.query("select * from messages where id = ?", id ) { rs, _ -> Message(rs.getString("id"), rs.getString("text")) }

Slide 29

Slide 29 text

fun findMessageById(id: String) = db.query("select * from messages where id = ?", id ) { rs, _ -> Message(rs.getString("id"), rs.getString("text")) } fun JdbcOperations.query(sql: String, vararg args: Any, function: (ResultSet, Int) -> T): List = query(sql, RowMapper { rs, i -> function(rs, i) }, *args) Extension function!

Slide 30

Slide 30 text

Summary Functions, Extensions, Trailing lambda,Varargs, SAM conversion

Slide 31

Slide 31 text

Scope functions

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

val dataSource = BasicDataSource() dataSource.driverClassName = "com.mysql.jdbc.Driver" dataSource.url = "jdbc:mysql://domain:3309/db" dataSource.username = "username" dataSource.password = "password" dataSource.maxTotal = 40 dataSource.maxIdle = 40 dataSource.minIdle = 4

Slide 34

Slide 34 text

val dataSource = BasicDataSource() dataSource.driverClassName = "com.mysql.jdbc.Driver" dataSource.url = "jdbc:mysql://domain:3309/db" dataSource.username = "username" dataSource.password = "password" dataSource.maxTotal = 40 dataSource.maxIdle = 40 dataSource.minIdle = 4 val dataSource = BasicDataSource().apply { driverClassName = "com.mysql.jdbc.Driver" url = "jdbc:mysql://domain:3309/db" username = "username" password = "password" maxTotal = 40 maxIdle = 40 minIdle = 4 }

Slide 35

Slide 35 text

val dataSource = BasicDataSource().apply { driverClassName = "com.mysql.jdbc.Driver" url = "jdbc:mysql://domain:3309/db" username = "username" password = "password" maxTotal = 40 maxIdle = 40 minIdle = 4 } public inline fun T.apply(block: T.() -> Unit): T { block() return this }

Slide 36

Slide 36 text

val dataSource = BasicDataSource().apply { driverClassName = "com.mysql.jdbc.Driver" url = "jdbc:mysql://domain:3309/db" username = "username" password = "password" maxTotal = 40 maxIdle = 40 minIdle = 4 } public inline fun T.apply(block: T.() -> Unit): T { block() return this } Lambda with receiver

Slide 37

Slide 37 text

?.let val order = retrieveOrder() if (order != null){ processCustomer(order.customer) }

Slide 38

Slide 38 text

val order = retrieveOrder() if (order != null){ processCustomer(order.customer) } retrieveOrder() ?. let { processCustomer(it.customer) } retrieveOrder() ?. customer ?. let { :: processCustomer } or ?.let

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

fun makeDir(path: String) = path.let { File(it) }.also { it.mkdirs() }

Slide 41

Slide 41 text

fun makeDir(path: String) = path.let { File(it) }.also { it.mkdirs() } Don’t overuse the scope functions!

Slide 42

Slide 42 text

fun makeDir(path: String) = path.let { File(it) }.also { it.mkdirs() } fun makeDir(path: String) : File { val file = File(path) file.mkdirs() return file } Don’t overuse the scope functions! This is simpler!

Slide 43

Slide 43 text

fun makeDir(path: String) = path.let { File(it) }.also { it.mkdirs() } fun makeDir(path: String) : File { val file = File(path) file.mkdirs() return file } Don’t overuse the scope functions! This is simpler! fun makeDir(path: String) = File(path).also { it.mkdirs() } OK, this one is actually fi ne :)

Slide 44

Slide 44 text

Default argument values and named parameters

Slide 45

Slide 45 text

fun find(name: String){ find(name, true) } fun find(name: String, recursive: Boolean){ } Function overloading

Slide 46

Slide 46 text

fun find(name: String){ find(name, true) } fun find(name: String, recursive: Boolean){ } fun find(name: String, recursive: Boolean = true){ } Default argument value Function overloading

Slide 47

Slide 47 text

fun find(name: String){ find(name, true) } fun find(name: String, recursive: Boolean){ } fun find(name: String, recursive: Boolean = true){ } fun main() { find("myfile.txt") } Default argument value Function overloading

Slide 48

Slide 48 text

class Figure( val width: Int = 1, val height: Int = 1, val depth: Int = 1, color: Color = Color.BLACK, description: String = "This is a 3d figure", ) Figure(Color.RED, "Red figure")

Slide 49

Slide 49 text

class Figure( val width: Int = 1, val height: Int = 1, val depth: Int = 1, color: Color = Color.BLACK, description: String = "This is a 3d figure", ) Figure(Color.RED, "Red figure") Compilation error

Slide 50

Slide 50 text

class Figure( val width: Int = 1, val height: Int = 1, val depth: Int = 1, color: Color = Color.BLACK, description: String = "This is a 3d figure", ) Figure(color = Color.RED, description = "Red figure")

Slide 51

Slide 51 text

Default argument values diminish the need for overloading in most cases.

Slide 52

Slide 52 text

Default argument values diminish the need for overloading in most cases. Named parameters is a necessary tool for working with default argument values

Slide 53

Slide 53 text

Expressions if when try

Slide 54

Slide 54 text

fun adjustSpeed(weather: Weather): Drive { val result: Drive if (weather is Rainy) { result = Safe() } else { result = Calm() } return result }

Slide 55

Slide 55 text

fun adjustSpeed(weather: Weather): Drive { val result: Drive if (weather is Rainy) { result = Safe() } else { result = Calm() } return result }

Slide 56

Slide 56 text

fun adjustSpeed(weather: Weather): Drive { val result: Drive = if (weather is Rainy) { Safe() } else { Calm() } return result }

Slide 57

Slide 57 text

fun adjustSpeed(weather: Weather): Drive { val result: Drive = if (weather is Rainy) { Safe() } else { Calm() } return result }

Slide 58

Slide 58 text

fun adjustSpeed(weather: Weather): Drive { return if (weather is Rainy) { Safe() } else { Calm() } }

Slide 59

Slide 59 text

fun adjustSpeed(weather: Weather): Drive { return if (weather is Rainy) { Safe() } else { Calm() } }

Slide 60

Slide 60 text

fun adjustSpeed(weather: Weather): Drive = if (weather is Rainy) { Safe() } else { Calm() }

Slide 61

Slide 61 text

fun adjustSpeed(weather: Weather): Drive = if (weather is Rainy) { Safe() } else { Calm() }

Slide 62

Slide 62 text

fun adjustSpeed(weather: Weather) = if (weather is Rainy) { Safe() } else { Calm() }

Slide 63

Slide 63 text

fun adjustSpeed(weather: Weather) = if (weather is Rainy) { Safe() } else { Calm() }

Slide 64

Slide 64 text

fun adjustSpeed(weather: Weather) = if (weather is Rainy) Safe() else Calm() Is it concise? Sure!

Slide 65

Slide 65 text

fun adjustSpeed(weather: Weather) = if (weather is Rainy) Safe() else Calm() Is it readable? It depends!

Slide 66

Slide 66 text

fun adjustSpeed(weather: Weather) = if (weather is Rainy) Safe() else Calm() What does the function return?

Slide 67

Slide 67 text

fun adjustSpeed(weather: Weather): Drive = ... fun adjustSpeed(weather: Weather) = ... For public API, keep the return type in the signature For private API it is generally OK to use type inference

Slide 68

Slide 68 text

fun adjustSpeed(weather: Weather) = if (weather is Rainy) Safe() else Calm()

Slide 69

Slide 69 text

abstract class Weather class Sunny : Weather() class Rainy : Weather() fun adjustSpeed(weather: Weather) = when (weather) { is Rainy -> Safe() else -> Calm() }

Slide 70

Slide 70 text

sealed class Weather class Sunny : Weather() class Rainy : Weather() fun adjustSpeed(weather: Weather) = when (weather) { is Rainy -> Safe() / / else -> Calm() }

Slide 71

Slide 71 text

sealed class Weather class Sunny : Weather() class Rainy : Weather() fun adjustSpeed(weather: Weather) = when (weather) { is Rainy -> Safe() / / else -> Calm() }

Slide 72

Slide 72 text

sealed class Weather class Sunny : Weather() class Rainy : Weather() fun adjustSpeed(weather: Weather) = when (weather) { is Rainy -> Safe() is Sunny -> TODO() }

Slide 73

Slide 73 text

sealed class Weather class Sunny : Weather() class Rainy : Weather() fun adjustSpeed(weather: Weather) = when (weather) { is Rainy -> Safe() is Sunny -> TODO() } Use expressions! Use when as expression body Use sealed classes with when

Slide 74

Slide 74 text

Null-safety

Slide 75

Slide 75 text

class Nullable { fun someFunction(){} } fun createNullable(): Nullable? = null fun main() { val n: Nullable? = createNullable() n.someFunction() }

Slide 76

Slide 76 text

class Nullable { fun someFunction(){} } fun createNullable(): Nullable? = null fun main() { val n: Nullable? = createNullable() n.someFunction() }

Slide 77

Slide 77 text

Consider using null-safe call val order = retrieveOrder() if (order == null || order.customer = = null || order.customer.address == null){ throw IllegalArgumentException("Invalid Order") } val city = order.customer.address.city

Slide 78

Slide 78 text

Consider using null-safe call val order = retrieveOrder() val city = order ?. customer ? . address ?. city

Slide 79

Slide 79 text

val order = retrieveOrder() val city = order ?. customer ? . address ?. city ?: throw IllegalArgumentException("Invalid Order") Consider using null-safe call

Slide 80

Slide 80 text

Avoid not-null assertions ! ! val order = retrieveOrder() val city = order !! .customer !! .address !! .city “You may notice that the double exclamation mark looks a bit rude: it’s almost like you’re yelling at the compiler. This is intentional.” - Kotlin in Action

Slide 81

Slide 81 text

Avoid not-null assertions ! ! class MyTest { class State(val data: String) private var state: State? = null @BeforeEach fun setup() { state = State("abc") } @Test fun foo() { assertEquals("abc", state !! .data) } }

Slide 82

Slide 82 text

Avoid not-null assertions ! ! class MyTest { class State(val data: String) private var state: State? = null @BeforeEach fun setup() { state = State("abc") } @Test fun foo() { assertEquals("abc", state !! .data) } } class MyTest { class State(val data: String) private lateinit var state: State @BeforeEach fun setup() { state = State("abc") } @Test fun foo() { assertEquals("abc", state.data) } } - use lateinit

Slide 83

Slide 83 text

No content

Slide 84

Slide 84 text

Use elvis operator class Person(val name: String?, val age: Int?) val p = retrievePerson() ?: Person()

Slide 85

Slide 85 text

Use elvis operator as return and throw class Person(val name: String?, val age: Int?) fun processPerson(person: Person) { val name = person.name if (name = = null) throw IllegalArgumentException("Named required") val age = person.age if (age == null) return println("$name: $age") }

Slide 86

Slide 86 text

Use elvis operator as return and throw class Person(val name: String?, val age: Int?) fun processPerson(person: Person) { val name = person.name if (name = = null) throw IllegalArgumentException("Named required") val age = person.age if (age == null) return println("$name: $age") }

Slide 87

Slide 87 text

Use elvis operator as return and throw class Person(val name: String?, val age: Int?) fun processPerson(person: Person) { val name = person.name if (name = = null) throw IllegalArgumentException("Named required") val age = person.age if (age == null) return println("$name: $age") }

Slide 88

Slide 88 text

Use elvis operator as return and throw class Person(val name: String?, val age: Int?) fun processPerson(person: Person) { val name = person.name ? : throw IllegalArgumentException("Named required") val age = person.age ?: return println("$name: $age") }

Slide 89

Slide 89 text

Consider using safe cast for type checking override fun equals(other: Any?) : Boolean { val command = other as Command return command.id == id }

Slide 90

Slide 90 text

Consider using safe cast for type checking override fun equals(other: Any?) : Boolean { val command = other as Command return command.id == id } override fun equals(other: Any?) : Boolean { return (other as? Command) ?. id == id }

Slide 91

Slide 91 text

Ranges

Slide 92

Slide 92 text

Use range checks instead of comparison pairs fun isLatinUppercase(c: Char) = c > = 'A' && c < = 'Z'

Slide 93

Slide 93 text

fun isLatinUppercase(c: Char) = c > = 'A' && c < = 'Z' Use range checks instead of comparison pairs

Slide 94

Slide 94 text

Use range checks instead of comparison pairs fun isLatinUppercase(c: Char) = c in 'A' . . 'Z'

Slide 95

Slide 95 text

Use range checks instead of comparison pairs fun isLatinUppercase(c: Char) = c in 'A' . . 'Z'

Slide 96

Slide 96 text

class Version(val major: Int, val minor: Int): Comparable { override fun compareTo(other: Version): Int { if (this.major != other.major) { return this.major - other.major } return this.minor - other.minor } } fun main() { val versionRange = Version(1, 11) . . Version(1, 30) println(Version(0, 9) in versionRange) println(Version(1, 20) in versionRange) } Comparable range

Slide 97

Slide 97 text

class Version(val major: Int, val minor: Int): Comparable { override fun compareTo(other: Version): Int { if (this.major != other.major) { return this.major - other.major } return this.minor - other.minor } } fun main() { val versionRange = Version(1, 11) . . Version(1, 30) println(Version(0, 9) in versionRange) println(Version(1, 20) in versionRange) } Comparable range public operator fun > T.rangeTo(that: T): ClosedRange = ComparableRange(this, that)

Slide 98

Slide 98 text

class Version(val major: Int, val minor: Int): Comparable { override fun compareTo(other: Version): Int { if (this.major != other.major) { return this.major - other.major } return this.minor - other.minor } } fun main() { val versionRange = Version(1, 11) . . Version(1, 30) println(Version(0, 9) in versionRange) println(Version(1, 20) in versionRange) } Comparable range public operator fun > T.rangeTo(that: T): ClosedRange = ComparableRange(this, that)

Slide 99

Slide 99 text

Ranges in loops fun main(args: Array) { for (i in 0 .. args.size - 1) { println("$i: ${args[i]}") } }

Slide 100

Slide 100 text

Ranges in loops fun main(args: Array) { for (i in 0 .. args.size - 1) { println("$i: ${args[i]}") } }

Slide 101

Slide 101 text

Ranges in loops fun main(args: Array) { for (i in 0 .. args.size - 1) { println("$i: ${args[i]}") } } for (i in 0 until args.size) { println("$i: ${args[i]}") }

Slide 102

Slide 102 text

Ranges in loops fun main(args: Array) { for (i in 0 .. args.size - 1) { println("$i: ${args[i]}") } } for (i in 0 until args.size) { println("$i: ${args[i]}") } for (i in args.indices) { println("$i: ${args[i]}") }

Slide 103

Slide 103 text

Ranges in loops fun main(args: Array) { for (i in 0 .. args.size - 1) { println("$i: ${args[i]}") } } for (i in 0 until args.size) { println("$i: ${args[i]}") } for (i in args.indices) { println("$i: ${args[i]}") } for ((i, arg) in args.withIndex()) { println("$i: $arg") }

Slide 104

Slide 104 text

No content

Slide 105

Slide 105 text

No content

Slide 106

Slide 106 text

No content

Slide 107

Slide 107 text

Standard library

Slide 108

Slide 108 text

abcd bcde cdef defg xya xyb xyc Group 1: Group 2: Count the total of characters that are present on each line in every group of strings

Slide 109

Slide 109 text

abcd bcde cdef defg xya xyb xyc Group 1: Group 2: Count the total of characters that are present on each line in every group of strings

Slide 110

Slide 110 text

abcd bcde cdef defg xya xyb xyc Group 1: Group 2: {d} {xy} Count the total of characters that are present on each line in every group of strings

Slide 111

Slide 111 text

abcd bcde cdef defg xya xyb xyc Group 1: Group 2: {d} {xy} count = 1 count = 2 Count the total of characters that are present on each line in every group of strings

Slide 112

Slide 112 text

abcd bcde cdef defg xya xyb xyc Group 1: Group 2: {d} {xy} count = 1 count = 2 Total = 3 Count the total of characters that are present on each line in every group of strings

Slide 113

Slide 113 text

val input = """ abcd bcde cdef defg xya xyb xyc """.trimIndent()

Slide 114

Slide 114 text

val input = """ abcd bcde cdef defg xya xyb xyc """.trimIndent() val groups: List = input.split("\n\n")

Slide 115

Slide 115 text

val input = """ abcd bcde cdef defg xya xyb xyc """.trimIndent() val groups: List = input.split("\n\n")

Slide 116

Slide 116 text

val input = """ abcd bcde cdef defg xya xyb xyc """.trimIndent() val groups: List = input.split("\n\n") var total = 0 for (group in groups) { val listOfSets: List> = group.split("\n").map(String :: toSet) var result = listOfSets.first() for (set in listOfSets) { result = result intersect set } total += result.count() }

Slide 117

Slide 117 text

val input = """ abcd bcde cdef defg xya xyb xyc """.trimIndent() val groups: List = input.split("\n\n") var total = 0 for (group in groups) { val listOfSets: List> = group.split("\n").map(String :: toSet) var result = listOfSets.first() for (set in listOfSets) { result = result intersect set } total += result.count() }

Slide 118

Slide 118 text

val input = """ abcd bcde cdef defg xya xyb xyc """.trimIndent() val groups: List = input.split("\n\n") var total = 0 for (group in groups) { val listOfSets: List> = group.split("\n").map(String :: toSet) var result = listOfSets.first() for (set in listOfSets) { result = result intersect set } total += result.count() }

Slide 119

Slide 119 text

val input = """ abcd bcde cdef defg xya xyb xyc """.trimIndent() val groups: List = input.split("\n\n") var total = 0 for (group in groups) { val listOfSets: List> = group.split("\n").map(String :: toSet) var result = listOfSets.first() for (set in listOfSets) { result = result intersect set } total += result.count() }

Slide 120

Slide 120 text

val input = """ abcd bcde cdef defg xya xyb xyc """.trimIndent() val groups: List = input.split("\n\n") var total = 0 for (group in groups) { val listOfSets: List> = group.split("\n").map(String :: toSet) var result = listOfSets.first() for (set in listOfSets) { result = result intersect set } total += result.count() } Transforming data

Slide 121

Slide 121 text

val input = """ abcd bcde cdef defg xya xyb xyc """.trimIndent() val groups: List = input.split("\n\n") var total = 0 for (group in groups) { val listOfSets: List> = group.split("\n").map(String :: toSet) var result = listOfSets.first() for (set in listOfSets) { result = result intersect set } total += result.count() } Calculating the result Transforming data

Slide 122

Slide 122 text

val groups: List = input.split("\n\n")

Slide 123

Slide 123 text

val groups: List = input.split("\n\n") // List>> val step1 = groups.map { it.split("\n").map(String :: toSet) } Transforming data

Slide 124

Slide 124 text

val groups: List = input.split("\n\n") // List>> val step1 = groups.map { it.split("\n").map(String :: toSet) } Transforming data

Slide 125

Slide 125 text

val groups: List = input.split("\n\n") // List>> val step1 = groups.map { it.split("\n").map(String :: toSet) } Transforming data

Slide 126

Slide 126 text

val groups: List = input.split("\n\n") // List>> val step1 = groups.map { it.split("\n").map(String :: toSet) } Transforming data

Slide 127

Slide 127 text

val groups: List = input.split("\n\n") // List>> val step1 = groups.map { it.split("\n").map(String :: toSet) } Transforming data

Slide 128

Slide 128 text

val groups: List = input.split("\n\n") // List>> val step1 = groups.map { it.split("\n").map(String :: toSet) } val step2 = step1.sumOf { it.reduce { a, b - > a intersect b }.count() } Transforming data Calculating the result

Slide 129

Slide 129 text

val groups: List = input.split("\n\n") // List>> val step1 = groups.map { it.split("\n").map(String :: toSet) } val step2 = step1.sumOf { it.reduce { a, b - > a intersect b }.count() } Calculating the result Transforming data

Slide 130

Slide 130 text

val groups: List = input.split("\n\n") // List>> val step1 = groups.map { it.split("\n").map(String :: toSet) } val step2 = step1.sumOf { it.reduce { a, b - > a intersect b }.count() } Calculating the result Transforming data

Slide 131

Slide 131 text

val groups: List = input.split("\n\n") // List>> val step1 = groups.map { it.split("\n").map(String :: toSet) } val step2 = step1.sumOf { it.reduce { a, b - > a intersect b }.count() } Calculating the result Transforming data

Slide 132

Slide 132 text

val groups: List = input.split("\n\n") // List>> val step1 = groups.map { it.split("\n").map(String :: toSet) } val step2 = step1.sumOf { it.reduce { a, b - > a intersect b }.count() } Calculating the result Transforming data

Slide 133

Slide 133 text

val groups: List = input.split("\n\n") // List>> val step1 = groups.map { it.split("\n").map(String :: toSet) } val step2 = step1.sumOf { it.reduce { a, b - > a intersect b }.count() } groups.map { group -> group.split(nl).map(String :: toSet) }.sumOf { answerSets -> answerSets.reduce { a, b -> a intersect b }.count() } Calculating the result Transforming data

Slide 134

Slide 134 text

No content

Slide 135

Slide 135 text

No content

Slide 136

Slide 136 text

kotlin { twitter = "@kotlin" youtube = "youtube.com/kotlin" slack = "slack.kotl.in" } me { name = "Anton Arhipov" twitter = "@antonarhipov" slides = "speakerdeck.com/antonarhipov" }