함수형 프로그래밍 스타일을 사용하면 컬렉션을 다룰 때 편리합니다. 대부분의 작업에 라이브러리 함수를 활용할 수 있고 그로 인해 코드를 이주 간결하게 만들 수 있습니다.
filter와 map은 컬렉션을 활용할 때 기반이 되는 함수입니다. 대부분의 컬렉션 연산을 이 두 함수를 통해 표현할 수 있습니다.
먼저 숫자를 사용한 예제와 Person을 사용한 예제를 통해 살펴보겠습니다:
data class Person(val name: String, val age: Int)
val people = listOf(Person("Alice", 29), Person("Bob", 31))
val over30 = people.filter { it.age > 30 }
println(over30) // [Person("Bob", 31)]
filter 함수는 컬렉션을 이터레이션하면서 주어진 람다에 각 원소를 넘겨서 람다가 true를 반환하는 원소만 모읍니다.
map 함수는 주어진 람다를 컬렉션의 각 원소에 적용한 결과를 모아서 새 컬렉션을 만듭니다.
println(people.map { it.name }) // [Alice, Bob]
println(people.map { it.age }) // [29, 31]
println(people.map { it.age > 30 }) // [false, true]
filter와 map은 체이닝하여 자주 사용됩니다:
people.filter { it.age > 30 }.map { it.name }
컬렉션의 모든 원소가 어떤 조건을 만족하는지 판단하거나 그 변종으로 컬렉션 안에 어떤 조건을 만족하는 원소가 있는지 판단하는 연산이 있습니다.
val canBeInClub27 = { p: Person -> p.age <= 27 }
// 모든 원소가 이 술어를 만족하는지
println(people.all(canBeInClub27))
// 술어를 만족하는 원소가 하나라도 있는지
println(people.any(canBeInClub27))
// 조건을 만족하는 원소의 개수
println(people.count(canBeInClub27))
// 조건을 만족하는 첫 번째 원소 찾기
println(people.find(canBeInClub27))
여기서 중요한 점은 count()
와 size
의 차이입니다:
// 이렇게 하면 중간 컬렉션이 생성됩니다
println(people.filter(canBeInClub27).size)
// count를 사용하면 중간 컬렉션이 생성되지 않습니다
println(people.count(canBeInClub27))
컬렉션의 모든 원소를 어떤 특성에 따라 여러 그룹으로 나누고 싶다면 groupBy를 사용합니다.
val people = listOf(Person("Alice", 31),
Person("Bob", 29),
Person("Carol", 31))
println(people.groupBy { it.age })
// 출력: {31=[Person(name=Alice, age=31), Person(name=Carol, age=31)],
// 29=[Person(name=Bob, age=29)]}
flatMap 함수는 두 가지 연산을 합친 함수입니다:
예를 들어, 책에 대한 정보를 저장하는 도서관이 있다고 가정해봅시다:
class Book(val title: String, val authors: List<String>)
val books = listOf(
Book("Thursday Next", listOf("Jasper Fforde")),
Book("Mort", listOf("Terry Pratchett")),
Book("Good Omens", listOf("Terry Pratchett", "Neil Gaiman"))
)
// 모든 저자의 집합을 얻기 위해 flatMap 사용
println(books.flatMap { it.authors }.toSet())
// 출력: [Jasper Fforde, Terry Pratchett, Neil Gaiman]
문자열 처리의 예:
val strings = listOf("abc", "def")
println(strings.flatMap { it.toList() })
// 출력: [a, b, c, d, e, f]
이 예제에서 람다 식 { it.toList() }
는 각 문자열을 문자 리스트로 변환합니다. flatMap은 이런 리스트들을 단일 리스트로 반환합니다.
리스트의 리스트가 있는데 모든 중첩된 리스트의 원소를 평평하게 펼치기만 하면 된다면, 특별히 변환해야 할 내용이 없다면 flatten을 사용할 수 있습니다.
val listOfLists = listOf(listOf(1, 2, 3), listOf(4, 5, 6))
println(listOfLists.flatten())
// 출력: [1, 2, 3, 4, 5, 6]