[Kotlin in Action] 23. 컬렉션 함수형 API (filter, map, all, any, count, find, groupBy, flatMap, flatten)

반응형
728x90
반응형

filter

결과 : 입력 컬렉션의 원소 중에서 주어진 술어(참/거짓을 반환하는 함수 : predicate) 를 만족하는 원소만으로 이뤄진 새로운 컬렉션

 

예제

Person 클래스 선언

data class Person(val name: String, val age: Int)

 

1) 짝수만 남긴다.

fun main() {
    val list = listOf(1, 2, 3, 4)
    println(list.filter { it % 2 == 0 }) // 짝수만 남는다.
}

 

2) 30살 이상인 사람만 출력한다.

fun main() {
    val people = listOf(Person("Alice", 27), Person("Bob", 31))
    println(people.filter { it.age > 30 }) // 30살 이상인 사람만 출력
}

 

3) 목록에 있는 사람들의 나이의 최댓값을 구하고, 그 최댓값과 같은 모든 사람을 반환한다.

fun main() {
    println(people.filter {it.age == people.maxByOrNull(Person::age) !!.age})
}

위 코드는 최댓값을 구하는 작업을 계속해서 반복한다. 한번만 반복하도록 하자.

fun main() {
	// 한번만 반복하도록 하자.
    val maxAge = people.maxByOrNull(Person::age) !!.age;
    println(people.filter { it.age == maxAge })
}

 

 

map()

- 주어진 람다를 컬렉션의 각 원소에 적용한 결과를 모아서 새 컬렉션을 만든다.
- 결과 : 원본 리스트와 원소의 개수는 같지만, 각 원소는 주어진 함수에 따라 변환된 새로운 컬렉션이다.

 

Person 클래스 선언

data class Person(val name: String, val age: Int)

 

1) 제곱이 모인 리스트로 변환하기

fun main() {
    // map
    val list2 = listOf(1, 2, 3, 4)
    println(list2.map { it * it }) // 제곱이 모인 리스트로 바꾼다.
}

 

2) 사람의 이름의 리스트로 변환하기

fun main() {
    val people2 = listOf(Person("Alice", 27), Person("Bob", 31))
    println(people2.map { it.name }) // 사람의 이름의 리스트로 변환한다.
    println(people2.map(Person::name)) // 멤버 참조 사용
}

 

3) 30살 이상인 사람의 이름을 출력하기

fun main() {
    // 30살 이상인 사람의 이름을 출력하기
    println(people.filter { it.age > 30 }.map { Person::name })
}

 

4) map의 value를 대문자로 바꾼 map으로 변환한다.

fun main() {
    // 맵의 경우 키와 값을 처리하는 함수가 따로 존재한다.
    val numbers = mapOf(0 to "zero", 1 to "one")
    // {0=ZERO, 1=ONE}
    println(numbers.mapValues { it.value.uppercase(Locale.getDefault()) })
}

 

 

all

모든 원소가 이 술어를 만족하는가?

val canBeInClub27 = { p: Person -> p.age <= 27 }
fun main() {
    val people = listOf(Person("Alice", 27), Person("Bob", 31))
    println(people.all(canBeInClub27)) // 모든 원소가 이 술어를 만족하는가?
}

 

 

any

이 술어를 만족하는 원소가 하나라도 있는가?

val canBeInClub27 = { p: Person -> p.age <= 27 }
fun main() {
    println(people.any(canBeInClub27))  //이 술어를 만족하는 원소가 하나라도 있는가?
}

 

 

!all 수행 결과 == 그 조건의 부정에 대해 any 수행 결과

fun main() {
    val list = listOf(1, 2, 3);
    println(!list.all { it == 3 } == list.any { it != 3 }) // 둘의 결과는 같다.
    println(!list.all { it == 3 }) // any를 사용하는게 더 낫다. (결과는 같으므로)
    println(list.any { it != 3 })
}

 

 

 

count

조건을 만족하는 개수를 반환한다.

val canBeInClub27 = { p: Person -> p.age <= 27 }
fun main() {
	val people = listOf(Person("Alice", 27), Person("Bob", 31))

    // count
    println(people.count(canBeInClub27)) // 1
}

 

 

find

조건을 만족하는 첫번째 원소를 반환한다.

val canBeInClub27 = { p: Person -> p.age <= 27 }
fun main() {
    val people = listOf(Person("Alice", 27), Person("Bob", 31))
    println(people.find(canBeInClub27))
    println(people.firstOrNull(canBeInClub27))
}
  • 술어를 만족하는 원소를 하나 찾고 싶다.
  • 가장 먼저 조건을 만족한다고 확인된 원소를 반환한다.
  • 만족하는 원소가 전혀 없는 경우 null을 반환한다. find는 firstOrNull과 같다.

 

 

groupBy

리스트를 여러 그룹으로 이뤄진 맵으로 변경한다.

data class Person(val name: String, val age: Int)
fun main() {
    val people = listOf(Person("Alice", 31), Person("Bob", 29), Person("Carol", 31))
    println(people.groupBy { it.age }) // 같은 나이끼리 묶는다.
}
  • 각 그룹은 리스트다. 
  • 따라서 groupBy의 결과 타입은 Map<Int, List<Person>>이다. 
  • 이 맵을 mapKeys, mapValues 등을 사용하여 변경할 수 있다.

 

확장 함수도 멤버 참조로 접근할 수 있다.

fun main() {
    val list = listOf("a", "ab", "b")
    println(list.groupBy(String::first)) // first 는 확장함수다. 멤버 참조로 접근할 수 있다.
}

 

 

 

flatMap

먼저 인자로 주어진 람다를 컬렉션의 모든 객체에 적용하고, 람다를 적용한 결과, 얻어지는 여러 리스트를 한 리스트로 모은다.

class Book(val title: String, val authors: List<String>)
fun main() {
    /*
               "abc" "def"
    "a", "b", "c"     "d", "e", "f"

      "a", "b", "c", "d", "e", "f"
     */
    val strings = listOf("abc", "def")
    println(strings.flatMap { it.toList() })
}
  • 1) toList() 적용 : 그 문자열에 속한 모든 문자로 이뤄진 리스트가 만들어진다.
  • 2) 리스트의 리스트에 들어있던 모든 원소를 단일 리스트로 반환한다.
"abc" "def"
"a" "b" "c" "d" "e" "f"
"a" "b" "c" "d" "e" "f"

 

fun main() {
    val books = listOf(Book("Thursday Next", listOf("Jasper Fforde")),
        Book("Mort", listOf("Terry Pratchett")),
        Book("Good Omens", listOf("Terry Pratchett", "Neil Gaiman")))

    books.flatMap { it.authors }.toSet(); // 책의 저자를 모두 모은 집합 사용
}
  • 1) flatMap : 모든 책의 작가를 평평한(문자열만으로 이뤄진) 리스트 하나로 모은다.
  • 2) toSet : 리스트에서 중복을 없애고 집합을 만든다.

 

특별히 변환해야할 내용이 없다면 리스트의 리스트를 평평하게 펼치기만 하면 되므로, listOfLists.flatten() 을 사용할 수 있다.

listOfLists.flatten()

 

 

반응형

Designed by JB FACTORY