본문 바로가기

Language/Kotlin

코틀린[Kotlin] Collections에서 자주 사용하는 함수 알아보기 (Filter, map, count, groupby ....)

지난번에 코틀린 collections에 대해 알아본 것에 이어서 컬렉션에서 자주 사용하는 함수들에 대해 알아보겠습니다. 함수들에 대해서 알면 알수록 개발이 편해지는 것 같습니다. 

이런 상황에선 이걸 쓰고, 저런 상황에서는 저걸 쓰고 등등등 

 

filter

Filter 함수를 사용하면 컬렉션을 필터링할 수 있습니다. 필터는 컬렉션의 각 요소에 적용됩니다. 조건을 true로 만드는 값 들이 결과 컬렉션에 담겨 반환됩니다.

val numbers = listOf(1, -2, 3, -4, 5, -6)      // 1
val positives = numbers.filter { x -> x > 0 }  // 2
val negatives = numbers.filter { it < 0 }      // 3
  1. 숫자 컬렉션을 정의합니다. 
  2. 양수를 가져옵니다. 
  3. it 표기법을 사용하여 음수를 가져옵니다.

map

맵 확장 함수를 사용하면 컬렉션의 모든 요소에 변형을 할 수 있습니다. 

val numbers = listOf(1, -2, 3, -4, 5, -6)     
val doubled = numbers.map { x -> x * 2 }      // 1
val tripled = numbers.map { it * 3 }          // 2
  1. 숫자에 2를 곱합니다.
  2. it 표기법을 사용하여 숫자에 3을 곱합니다.

any, all, none

any, all, none 함수들은 주어진 조건에 일치하는 컬렉션 요소의 존재를 확인합니다.

any 함수는 컬렉션에 주어진 조건에 일치하는 요소가 하나 이상 포함되어 있으면 true를 반환합니다.

all 함수는 컬렉션의 모든 요소가 주어진 술어와 일치하면 true를 반환합니다.

none 함수는 컬렉션의 모든 요소가 주어진 조건에 일치하지 않으면 true를 반환합니다.

val numbers = listOf(1, -2, 3, -4, 5, -6)        

val anyNegative = numbers.any { it < 0 }            
val anyGT6 = numbers.any { it > 6 }

val allEven = numbers.all { it % 2 == 0 }           
val allLess6 = numbers.all { it < 6 }                

val noneEven = numbers.none { it % 2 == 1 }           
val noneLess6 = numbers.none { it > 6 }

find, findLast

find 및 findLast 함수는 주어진 조건에 일치하는 첫 번째 또는 마지막 컬렉션 요소를 반환합니다. 조건을 충족하는 값이 없으면 함수는 null을 반환합니다.

val words = listOf("Lets", "find", "something", "in", "collection", "somehow") 

// "something" 반환함
val first = words.find { it.startsWith("some") }                                // 2
// "somehow" 반환함
val last = words.findLast { it.startsWith("some") }                             // 3
// null 반환함
val nothing = words.find { it.contains("nothing") }

first, last

first, last 함수는 해당 컬렉션의 첫 번째 및 마지막 요소를 반환합니다. 람다 함수와 같이 사용할 수 있는데, 이 경우 조건에 일치하는 첫 번째 또는 마지막 요소를 반환합니다

컬렉션이 비어 있거나 조건에 일치하는 요소가 포함되어 있지 않으면 함수에서 NoSuchElementException이 발생합니다

val numbers = listOf(1, -2, 3, -4, 5, -6)           

// 1
val first = numbers.first()                       
// -6
val last = numbers.last()                        
// -2
val firstEven = numbers.first { it % 2 == 0 }       
// 5
val lastOdd = numbers.last { it % 2 != 0 }
  • firstOrNull, lastOrNull

이 함수는 한 가지 차이점이 있지만 거의 동일한 방식으로 작동합니다. 일치하는 요소가 없으면 null을 반환합니다.

val words = listOf("foo", "bar", "baz", "faz")        
val empty = emptyList<String>()                       
// null을 반환함
val first = empty.firstOrNull()                       
// null을 반환함
val last = empty.lastOrNull()                         

// "foo"를 반환함
val firstF = words.firstOrNull { it.startsWith('f') }  
// null을 반환함
val firstZ = words.firstOrNull { it.startsWith('z') }  
// null을 반환함
val lastF = words.lastOrNull { it.endsWith('f') }      
// "faz"을 반환함
val lastZ = words.lastOrNull { it.endsWith('z') }

count

count 함수는 컬렉션의 총요소의 수 또는 주어진 조건과 일치하는 요소 수를 반환합니다.

val numbers = listOf(1, -2, 3, -4, 5, -6)            
// 6
val totalCount = numbers.count()                     
// 3
val evenCount = numbers.count { it % 2 == 0 }

associateBy, groupBy

함수 AssociateBy 및 groupBy는 컬렉션의 요소에서 지정된 키로 인덱싱 된 맵을 만들어 줍니다. 키는 keySelector 매개변수에 정의되어 있습니다. 또한 선택적인 valueSelector를 지정하여 맵의 값에 저장할 항목을 정의할 수 있습니다.

 

associateBy와 groupBy의 차이점은 동일한 키로 개체를 처리하는 방법입니다.
associateBy는 적합한 요소들 중 마지막 요소만을 값으로 사용합니다. 

groupBy는 모든 적절한 요소들을 리스트로 만들어 값에 넣습니다.

반환된 맵은 원래 컬렉션의 반복 순서를 유지합니다.

data class Person(val name: String, val city: String, val phone: String) // 1

val people = listOf(                                                     // 2
    Person("John", "Boston", "+1-888-123456"),
    Person("Sarah", "Munich", "+49-777-789123"),
    Person("Svyatoslav", "Saint-Petersburg", "+7-999-456789"),
    Person("Vasilisa", "Saint-Petersburg", "+7-999-123456"))

// {+1-888-123456=Person(name=John, city=Boston, phone=+1-888-123456), ...}
val phoneBook = people.associateBy { it.phone }                          // 3
// {+1-888-123456=Boston, +49-777-789123=Munich,...}
val cityBook = people.associateBy(Person::phone, Person::city)           // 4
// {Boston=[John], Munich=[Sarah], Saint-Petersburg=[Svyatoslav, Vasilisa]}
val peopleCities = people.groupBy(Person::city, Person::name)            // 5
// {Boston=John, Munich=Sarah, Saint-Petersburg=Vasilisa}
val lastPersonCity = people.associateBy(Person::city, Person::name)      // 6

3. 키가 phone 문자열이고, 값이 Person 객체인 맵을 만듭니다. it.phone은 여기에서 keySelector이고, valueSelector는 제공되지 않았으므로 맵의 값은 Person 객체입니다.

4. Person::city는 여기서 valueSelector이므로 맵의 값에는 city만 포함됩니다.

5. 키가 city이고, name이 값인 맵를 만듭니다. groupBy 함수는 키인 "Saint-Petersburg"가 중복돼서 해당하는 값을 리스트로 만든 것을 알 수 있습니다.

6. 키가 city이고, name이 값인 맵를 만듭니다. associateBy 함수는 키인 "Saint-Petersburg"가 중복돼서 해당하는 마지막 값만 가지고 온 것을 확인할 수 있습니다.

partition

파티션 함수는 주어진 조건을 사용하여 원래 컬렉션을 한 쌍의 리스트로 분할합니다.

1. 조건이 참인 요소들,

2. 조건이 거짓인 요소들 입니다

val numbers = listOf(1, -2, 3, -4, 5, -6)                // 1

val evenOdd = numbers.partition { it % 2 == 0 }           // 2
val (positives, negatives) = numbers.partition { it > 0 } // 3

2. 짝수와 홀수가 있는 리스트 쌍으로 숫자를 나눕니다.

3. 숫자를 양수와 음수가 있는 두 개의 목록으로 나눕니다. Pair Destructuring은 Pair 멤버를 얻기 위해 여기에 적용됩니다.

flatMap

flatMap은 컬렉션의 각 요소를 반복 가능한 개체로 변환하고 변환 결과들을 단일 리스트로 작성합니다.

val fruitsBag = listOf("apple","orange","banana","grapes")  // 1
val clothesBag = listOf("shirts","pants","jeans")           // 2
val cart = listOf(fruitsBag, clothesBag)                    // 3
// [[apple, orange, banana, grapes], [shirts, pants, jeans]]
val mapBag = cart.map { it }                                // 4
// [apple, orange, banana, grapes, shirts, pants, jeans]
val flatMapBag = cart.flatMap { it }

4. 두 개의 리스트를 가진 리스트를 반환함 

5. 두 리스트의 요소로 구성된 단일 리스트를 반환함

minOrNull, maxOrNull

minOrNull 및 maxOrNull 함수는 컬렉션의 가장 작은 요소와 가장 큰 요소를 반환합니다. 컬렉션이 비어 있으면 null을 반환합니다.

val numbers = listOf(1, 2, 3)
val empty = emptyList<Int>()
val only = listOf(3)

// 1, 3 을 반환함
println("Numbers: $numbers, min = ${numbers.minOrNull()} max = ${numbers.maxOrNull()}") 
// null을 반환함
println("Empty: $empty, min = ${empty.minOrNull()}, max = ${empty.maxOrNull()}")   
// 3을 반환함 
println("Only: $only, min = ${only.minOrNull()}, max = ${only.maxOrNull()}")

sorted

sorted는 오름차순에 따라 정렬된 컬렉션 요소 리스트를 반환합니다.

sortedBy는 지정된 셀렉터 함수에서 반환된 값의 정렬 순서의 오름차순에 따라 요소를 정렬합니다.

val shuffled = listOf(5, 4, 2, 1, 3, -10)                  
// [-10, 1, 2, 3, 4, 5]
val natural = shuffled.sorted()                             
// [5, 4, 3, 2, 1, -10]
val inverted = shuffled.sortedBy { -it }                    
// [5, 4, 3, 2, 1, -10]
val descending = shuffled.sortedDescending()                
// [-10, 5, 4, 3, 2, 1]
val descendingBy = shuffled.sortedByDescending { abs(it)  }

마지막의 경우 절댓값을 적용하고 비교했습니다.

Map Element Access

맵에 적용될 때 [] 연산자는 주어진 키에 해당하는 값을 반환하거나 맵에 그러한 키가 없으면 null을 반환합니다.

 

getValue 함수는 주어진 키에 해당하는 기존 값을 반환하거나 키를 찾을 수 없는 경우 예외를 throw 합니다. withDefault로 생성된 맵의 경우 getValue는 예외를 발생시키는 대신 기본값을 반환합니다.

val map = mapOf("key" to 42)

val value1 = map["key"]                                     // 1
val value2 = map["key2"]                                    // 2

val value3: Int = map.getValue("key")                       // 1

val mapWithDefault = map.withDefault { k -> k.length }
val value4 = mapWithDefault.getValue("key2")                // 3

try {
    map.getValue("anotherKey")                              // 4
} catch (e: NoSuchElementException) {
    println("Message: $e")
}

2. "key2"가 맵에 없기 때문에 null을 반환합니다.

3. "key2"가 없기 때문에 기본값을 반환합니다. 이 키의 경우 4입니다.

4. "anotherKey"가 맵에 없기 때문에 NoSuchElementException이 발생합니다.

zip

zip 함수는 두 개의 지정된 컬렉션을 새 컬렉션으로 병합합니다. 기본적으로 결과 컬렉션에는 인덱스가 동일한 소스 컬렉션 요소 Pair가 포함됩니다. 그러나 결과 컬렉션 요소의 고유한 구조를 정의할 수 있습니다.

결과 컬렉션의 크기는 소스 컬렉션의 최소 크기와 같습니다.

val A = listOf("a", "b", "c")                  // 1
val B = listOf(1, 2, 3, 4)                     // 1

// [(a, 1), (b, 2), (c, 3)]
val resultPairs = A zip B                      // 2
// [a1, b2, c3]
val resultReduce = A.zip(B) { a, b -> "$a$b" } // 3

2. 두 개의 리스트를 Pair 값을 한 개의 리스트로 병합합니다. infix 표기법이 사용됩니다.

3. 주어진 변환에 의해 문자열을 값으로 가진 한 개의 리스트로 병합합니다.

두 경우 모두 결과 컬랙션의 크기는 소스 컬렉션의 최소 크기랑 동일합니다.

getOrElse

getOrElse는 컬렉션 요소에 대한 안전한 접근을 제공합니다. 인덱스와 인덱스가 범위를 벗어난 경우 기본값을 제공하는 함수를 사용합니다

val list = listOf(0, 10, 20)
println(list.getOrElse(1) { 42 })    // 1
println(list.getOrElse(10) { 42 })   // 2

2. 인덱스 10이 리스트의 범위를 벗어났기 때문에 기본값으로 정의된 42를 출력합니다.

 

getOrElse는 Map에 적용하여 주어진 키의 값을 얻을 수도 있습니다.

val map = mutableMapOf<String, Int?>()
println(map.getOrElse("x") { 1 })       // 1

map["x"] = 3
println(map.getOrElse("x") { 1 })       // 2

map["x"] = null
println(map.getOrElse("x") { 1 })       // 3

1. 키 "x"가 맵에 없기 때문에 기본값을 출력합니다.

2. 키 "x"의 값인 3을 출력합니다.

3. 키 "x"에 대한 값이 정의되지 않았기 때문에 기본값을 출력합니다.