[출처 - Kotlin In Action] [아래 내용들은 Kotlin In Action을 공부하면서 스스로 정리한 내용입니다]
코틀린의 장점은 다시 생각해봐도 일부 기능을 컴파일러에서 대신 해주는것과 다양한 라이브러리 함수에 있는것 같습니다. 그중에서 컬렉션을 다루는 코틀린 표준 라이프러리 함수를 확인해 보겠습니다.
filter와 map
filter 함수는 뜻 그대로 컬렉션을 이터레이션하면서 주어진 람다에 각 원소를 넘겨서 람다가 true를 반환하는 원소만 모은다.
fun main(args: Array<String>) {
val list = listOf(1, 2, 3, 4)
println(list.filter { it % 2 == 0 })
}
위에 list함수의 값을 filter하는데 그 값을 2로 나누었을 때 나머지가 0인 값만 (즉, 짝수인 값만) 나타내게 됩니다.
단순 변수뿐만 아니라 다음과 같은 class 값도 filter 할 수 있습니다.
data class Person(val name: String, val age: Int)
fun main(args: Array<String>) {
val people = listOf(Person("Alice", 29), Person("Bob", 31))
println(people.filter { it.age > 30 })
}
위의 코드를 보면 Person에 대한 filter를 할 수 있는데, Person의 데이터인 name과 age로도 필터를 할 수 있는데 위 코드는 나이로 filter를 하고 있습니다.
하지만 filter는 조건에 대한 filter만 할 수 있지 따로 수정은 불가능 합니다. 수정을 하기위해서는 map을 사용해야 합니다.
map함수는 주어진 람다를 컬렉션의 각 원소에 적용한 결과를 모아서 새 컬렉션으로 만들어 줍니다.
fun main(args: Array<String>) {
val list = listOf(1, 2, 3, 4)
println(list.map { it * it })
}
위 코드를 보면 리스트안에 숫자들이 있고, map을 통하여 각 변수를 제곱하여 그 결과를 다시 반환합니다.
결과는 [1,4,9,16]입니다.
그리고 위의 Person을 map을 사용하면
fun main(args: Array<String>) {
val people = listOf(Person("Alice", 29), Person("Bob", 31))
println(people.map { it.name })
}
[Alice, Bob]
이렇게 변경할 수 있습니다. 만일 map을 사용하지 않았다면
[Person(name=Alice, age=29), Person(name=Bob, age=31)]
이런 결과가 나왔을 것입니다.
또한, 위의 map은 멤버참조를 사용하여
fun main(args: Array<String>) {
val people = listOf(Person("Alice", 29), Person("Bob", 31))
println(people.map(Person::name))
}
이렇게도 사용 가능 합니다. 또한,
data class Person(val name: String, val age: Int)
fun main(args: Array<String>) {
val people = listOf(Person("Alice", 29), Person("Bob", 31))
println(people.filter{ it.age== people.maxBy(Person::age)!!.age}.map(Person::name))
}
이렇게 filter후 결과를 map을 통하여 다시 출력할 수 있으며 반대로 map으로 변경 후 filter로 출력 할 수도 있습니다. (위의 결과는 Bob 입니다. 그리고 람다를 인자로 받는 함수에 람다를 넘기면 겉으로 볼때는 단순해 보이지만 내부 로직의 복잡도로 인해 실제로는 불합리한 계산이 될때도 있으니 코드를 작성할 때 어떤 일이 벌어 질지 고민해야합니다.
그러므로 위의 로직은
val people = listOf(Person("Alice", 29), Person("Bob", 31))
val maxAge = people.maxBy(Person::age)!!.age
println(people.filter{ it.age==maxAge}.map(Person::name))
이렇게 변경하는것이 더 나은 동작성을 보일 수 있습니다.)
그리고 filter와 map 관련 라이브러리 함수중 key를 처리하는 filterkey와 mapkey, 그리고 값을 처리하는 filterValue, mapValue가 있습니다.
fun main(args: Array<String>) {
val numbers = mapOf(0 to "zero", 1 to "one")
println(numbers.mapValues { it.value.toUpperCase() })
}
// 결과 {0=ZERO, 1=ONE}
fun main(args: Array<String>) {
val numbers = mapOf(0 to "zero", 1 to "one")
println(numbers.mapKeys { it.value.toUpperCase() })
}
// 결과 {ZERO=zero, ONE=one}
위의 mapValues를 통하여 Value의 값을 수정하는 것이고 mapKeys를 통하여 Key 값을 수정하게 됩니다.
all, any, count, find
fun main(args: Array<String>) {
val list = listOf(1, 2, 3)
println(list.all { it == 3 })
println(list.any { it == 3 })
}
all의 경우는 모든 원소가 만족할 경우 true 아닐 경우 false 이며 any의 경우에는 원소중 1개라도 만족하면 true입니다.(위의 결과는
false
true
입니다.) 그리고 !all은 any와 같은 결과를 나타냅니다.
data class Person(val name: String, val age: Int)
val canBeInClub27 = { p: Person -> p.age <= 27 }
fun main(args: Array<String>) {
val people = listOf(Person("Alice", 27), Person("Bob", 31))
println(people.find(canBeInClub27))
}
find는 조건을 만족하는 첫번 째 원소를 반환합니다. (위의 결과는
Person(name=Alice, age=27)
입니다.) 그리고 findLast라는 함수도 있는데 이는 조건을 만족하는 마지막 원소를 반환 합니다.
count는 조건을 만족하는 원소의 갯수를 세는 함수입니다.
data class Person(val name: String, val age: Int)
val canBeInClub27 = { p: Person -> p.age <= 27 }
fun main(args: Array<String>) {
val people = listOf(Person("Alice", 27), Person("Bob", 31),Person("Cris", 27))
println(people.count(canBeInClub27))
}
이렇게 할 경우 2라는 결과를 받을 수 있습니다.
groupBy
groupBy는 조건에 맞춰서 그룹을 나누는 함수입니다.
data class Person(val name: String, val age: Int)
fun main(args: Array<String>) {
val people = listOf(Person("Alice", 31),
Person("Bob", 29), Person("Carol", 31))
println(people.groupBy { it.age })
}
위 코드는 나이로 그룹을 나눕니다. 그래서 29세와 31세로 구룹을 나누게 됩니다. 그래서 결과는
{31=[Person(name=Alice, age=31), Person(name=Carol, age=31)], 29=[Person(name=Bob, age=29)]}
이렇게 나옵니다. 사용할 때는 gropBy를 할 경우 Map<key,List>의 형식으로 됩니다. 그래서 위의 groupBy는 Map<Int,List<Person>> 타입으로 생성이되고 이에 맞춰서 사용하시면 됩니다.
또한 멤버참조를 통하여 다음과 같이 사용도 가능합니다.
fun main(args: Array<String>) {
val list = listOf("a", "ab", "b")
//println(list.groupBy{it.toString().first()})
println(list.groupBy(String::first))
}
그리고 위의 groupBy 타입은 Map<Char, List<String>> 입니다.
flatMap
flatMap 함순는 먼저 인자로 주어진 람다를 컬렉션의 모든 객체에 적용하고 람다를 적용한 결과 얻어지는 여러 리스트를 한 리스트로 한데 모읍니다.
fun main(args: Array<String>) {
val strings = listOf("abc", "def")
val a = strings[0].toList()
val b = strings[1].toList()
println("strings[0].toList(): ${strings[0].toList()}")
println("strings[1].toList(): ${strings[1].toList()}")
println(strings.flatMap { it.toList() })
}
위으 코드를 보면 toList를 사용하고 있습니다. toList 함수를 문자열에 적용하면 그 문자열에 속한 모든 문자로 이뤄진 리스트가 만들어 집니다. 그리고 flatMap을 통하여 그렇게 만들어진 list를 합칩니다. 그래서 결과를 보면
strings[0].toList(): [a, b, c]
strings[1].toList(): [d, e, f]
[a, b, c, d, e, f]
결과가 나옵니다.
class Book(val title: String, val authors: List<String>)
fun main(args: Array<String>) {
val books = listOf(Book("Thursday Next", listOf("Jasper Fforde")),
Book("Mort", listOf("Terry Pratchett")),
Book("Good Omens", listOf("Terry Pratchett",
"Neil Gaiman")))
println(books.flatMap { it.authors })
}
위 코드를 보면 Book에 책의 이름과 저자들을 작성합니다. 이때 저자는 한명이 아니고 여러명일 수 있습니다. 그래서 flatMap을 통하여 autors를 작가를 모으고, toSet을 통하여 중첩된 내용을 삭제합니다. 결과를 보면
// toSet 적용
[Jasper Fforde, Terry Pratchett, Neil Gaiman]
// toSet 미적용
[Jasper Fforde, Terry Pratchett, Terry Pratchett, Neil Gaiman]
이렇게 나옵니다.
'2023년 이전 > kotlin' 카테고리의 다른 글
kotlin - 자바 함수형 인터페이스 활용 (2) | 2020.03.10 |
---|---|
kotlin - 시퀀스(Sequence) (0) | 2020.03.02 |
kotlin - 람다식 (0) | 2020.02.26 |
kotlin - object 키워드 (0) | 2020.02.25 |
kotlin - 데이터 클래스와 클래스 위임 (0) | 2020.02.25 |