-
Notifications
You must be signed in to change notification settings - Fork 0
Kotlin 컬렉션 함수형 API 학습 테스트 (Transformation, Grouping, Combination, ControlFlow 확장 함수) #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
6491153
Add ListFunctionalApi with basic examples of Kotlin collection functions
sh1mj1 e082580
test(MappingTest): mapping api for Kotlin List
sh1mj1 5bdbdc0
test(MappingTest): add mapTo type methods
sh1mj1 53aba00
test(MappingTest): add flatMapTo type methods
sh1mj1 2e866eb
test(MappingTest): add associateTo type methods
sh1mj1 b5ba82c
test(FlatteningTest): add flattening type methods test
sh1mj1 006edd0
test(FilteringTest): add filtering and collection manipulation method…
sh1mj1 7b8589b
test(FilteringTest): add tests for Map type's filtering methods
sh1mj1 b0ecd5c
test(FlatteningTest): update test descriptions for clarity on flatten…
sh1mj1 c7da45b
test(FlatteningTest): add comprehensive tests for flattening methods …
sh1mj1 5f06438
test(FilteringTest, FlatteningTest, MappingTest): update variable dec…
sh1mj1 8233cc3
chore(ListFunctionalApiTest.kt): remove
sh1mj1 e8b0343
test(FlatteningTest): improve test description and update map flatten…
sh1mj1 3d766d4
test(MappingTest): refactor mapIndexedNotNull logic for improved read…
sh1mj1 2fad10d
test(SearchingTest): add comprehensive tests for list searching methods
sh1mj1 aeb0a7f
test(SearchingTest): add more indexOf tests
sh1mj1 6d4fd4c
test(SearchingTest): add more indexOf tests
sh1mj1 4f65083
test(SortingTest): add comprehensive tests for list sorting methods
sh1mj1 f8fd893
test(SortingTest): add tests for sortedWith functionality with variou…
sh1mj1 7bc7c00
test(SortingTest): sortedWith 테스트 추가
sh1mj1 e9ad6d6
test(SortingTest): 기존 리스트 정렬 메서드에 대한 포괄적인 테스트 추가
sh1mj1 16d4bfb
test(SortingTest): map 타입의 정렬
sh1mj1 6a79e5e
feat(AggregationTest): aggregation api
sh1mj1 304000f
refactor(AggregationTest): rename variables
sh1mj1 50269fe
refactor(AggregationTest): specify some variable's type
sh1mj1 e9a69cc
fix(AggregationTest): 테스트 이름 수정, runningFold 실패하는 테스트 수정
sh1mj1 7c8b0ca
test(GroupingTest): 리스트 그루핑 메서드 테스트
sh1mj1 19bec61
test(TransformationTest): List 변형 메서드
sh1mj1 f5a9d7d
test(TransformationTest): Set 변형 메서드
sh1mj1 3a74c0e
test(TransformationTest): Map transformation 메서드
sh1mj1 84c7852
test(CombinationTest): 컬렉션 조합 연산에 대한 테스트 추가
sh1mj1 b4ae358
test(ControlFlowExtensionTest): Kotlin 스코프 함수 및 조건부 함수 학습
sh1mj1 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
203 changes: 203 additions & 0 deletions
203
app/src/test/java/com/example/learningtest/collection/functional/AggregationTest.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,203 @@ | ||
| package com.example.learningtest.collection.functional | ||
|
|
||
| import io.kotest.assertions.throwables.shouldThrow | ||
| import io.kotest.core.spec.style.FreeSpec | ||
| import io.kotest.matchers.shouldBe | ||
|
|
||
| class AggregationTest : FreeSpec({ | ||
| data class User( | ||
| val name: String, | ||
| val age: Int, | ||
| ) | ||
|
|
||
| "List Aggregation" - { | ||
|
|
||
| "count - 컬렉션의 요소 개수" { | ||
| val numbers: List<Int> = listOf(1, 2, 3, 4, 5) | ||
|
|
||
| numbers.count() shouldBe 5 | ||
| numbers.count { it % 2 == 0 } shouldBe 2 | ||
| } | ||
|
|
||
| "sum - 컬렉션의 숫자 요소 합계" { | ||
| val numbers: List<Int> = listOf(1, 2, 3, 4, 5) | ||
|
|
||
| numbers.sum() shouldBe 15 | ||
| } | ||
|
|
||
| "sumOf - 컬렉션의 특정 속성을 기준으로 숫자 요소 합계" { | ||
| val users: List<User> = | ||
| listOf( | ||
| User("Alice", 30), | ||
| User("Bob", 25), | ||
| User("Charlie", 35), | ||
| ) | ||
|
|
||
| users.sumOf { user -> | ||
| user.age.takeIf { age -> age >= 30 } ?: 0 | ||
| } shouldBe 65 | ||
| } | ||
|
|
||
| "average - 컬렉션의 숫자 요소 평균" { | ||
| val numbers: List<Double> = listOf(1.0, 2.0, 3.0, 4.0, 5.0) | ||
|
|
||
| numbers.average() shouldBe 3.0 | ||
| emptyList<Double>().average() shouldBe Double.NaN | ||
| } | ||
|
|
||
| "max - 컬렉션에서 가장 큰 요소(빈 컬렉션이면 NoSuchElementException 예외 발생)" { | ||
| val numbers: List<Int> = listOf(10, 5, 20, 15) | ||
|
|
||
| numbers.max() shouldBe 20 | ||
| shouldThrow<NoSuchElementException> { | ||
| emptyList<Int>().max() | ||
| } | ||
| } | ||
|
|
||
| "maxOrNull - 컬렉션에서 가장 큰 요소 (없으면 null)" { | ||
| val numbers: List<Int> = listOf(10, 5, 20, 15) | ||
|
|
||
| numbers.maxOrNull() shouldBe 20 | ||
| emptyList<Int>().maxOrNull() shouldBe null | ||
| } | ||
|
|
||
| "maxOf - 특정 속성 기준으로 컬렉션에서 가장 큰 요소 (빈 컬렉션이면 예외)" { | ||
| val numbers: List<User> = | ||
| listOf( | ||
| User("Alice", 30), | ||
| User("Bob", 25), | ||
| User("Charlie", 35), | ||
| ) | ||
|
|
||
| numbers.maxOf { user -> user.age } shouldBe 35 | ||
| } | ||
|
|
||
| "maxOfOrNull - 특정 속성 기준으로 컬렉션에서 가장 큰 요소 (없으면 null)" { | ||
| val users: List<User> = | ||
| listOf( | ||
| User("Alice", 30), | ||
| User("Bob", 25), | ||
| User("Charlie", 35), | ||
| ) | ||
|
|
||
| users.maxOfOrNull(User::age) shouldBe 35 | ||
| emptyList<User>().maxOfOrNull(User::age) shouldBe null | ||
| } | ||
|
|
||
| "minOfOrNull - 특정 속성 기준으로 컬렉션에서 가장 작은 요소 (없으면 null)" { | ||
| val users: List<User> = | ||
| listOf( | ||
| User("Alice", 30), | ||
| User("Bob", 25), | ||
| User("Charlie", 35), | ||
| ) | ||
|
|
||
| users.minOfOrNull(User::age) shouldBe 25 | ||
| emptyList<User>().minOfOrNull(User::age) shouldBe null | ||
| } | ||
|
|
||
| "reduce - 첫 번째 요소부터 시작하여 누적 연산 (빈 컬렉션에는 사용 불가)" { | ||
| val numbers: List<Int> = listOf(1, 2, 3, 4) | ||
|
|
||
| // acc: 0, number: 1 -> 1 | ||
| // acc: 1, number: 2 -> 3 | ||
| // acc: 3, number: 3 -> 2 | ||
| // acc: 2, number: 4 -> 6 | ||
| numbers.reduce { acc, number -> | ||
| if (acc >= number) acc - 1 else acc + number | ||
| } shouldBe 6 | ||
|
|
||
| val words: List<String> = listOf("hello", " ", "world") | ||
|
|
||
| words.reduce { acc, word -> acc + word } shouldBe "hello world" | ||
|
|
||
| shouldThrow<UnsupportedOperationException> { | ||
| emptyList<Int>().reduce { acc, i -> acc + i } | ||
| } | ||
| } | ||
|
|
||
| "fold - 초기값을 가지고 누적 연산(빈 리스트도 가능)" { | ||
| val numbers: List<Int> = listOf(1, 2, 3, 4) | ||
| val initialValue = 10 | ||
|
|
||
| // acc: 10, number: 1 -> 11 | ||
| // acc: 11, number: 2 -> 11 | ||
| // acc: 11, number: 3 -> 14 | ||
| // acc: 14, number: 4 -> 18 | ||
| numbers.fold(initialValue) { acc, number -> | ||
| if (number % 2 == 0) acc else acc + number | ||
| } | ||
|
|
||
| val words: List<String> = listOf("hello", " ", "world") | ||
| val initialString = "Start: " | ||
| words.fold(initialString) { acc, s -> acc + s } shouldBe "Start: hello world" | ||
|
|
||
| emptyList<Int>().fold(0) { accumulator, element -> accumulator + element } shouldBe 0 | ||
| } | ||
|
|
||
| "runningReduce - 각 단계의 누적 결과를 포함하는 리스트 반환 (빈 컬렉션에는 사용 불가)" { // New test case | ||
| val numbers: List<Int> = listOf(1, 2, 3, 4) | ||
|
|
||
| val runningSum: List<Int> = numbers.runningReduce { acc, number -> acc + number } | ||
| runningSum shouldBe listOf<Int>(1, 3, 6, 10) // 1, 1 + 2, 1 + 2 + 3, 1 + 2 + 3 + 4 | ||
| } | ||
|
|
||
| "runningFold - 초기값과 함께 각 단계의 누적 결과를 포함하는 리스트 반환 (빈 리스트도 가능)" { // New test case | ||
| val numbers: List<Int> = listOf(1, 2, 3, 4) | ||
|
|
||
| val runningNumbersFolded: List<Int> = | ||
| numbers.runningFold(10) { acc, number -> acc + number } | ||
| runningNumbersFolded shouldBe listOf(10, 11, 13, 16, 20) | ||
|
|
||
| val words: List<String> = listOf("a", "b", "c") | ||
|
|
||
| val runningWordsFolded: List<String> = | ||
| words.runningFold("Start: ") { acc, word -> acc + word } | ||
| runningWordsFolded shouldBe listOf("Start: ", "Start: a", "Start: ab", "Start: abc") | ||
| } | ||
| } | ||
|
|
||
| "Map Aggregation" - { | ||
| val map: Map<String, Int> = mapOf("a" to 1, "b" to 2, "c" to 3, "d" to 4) | ||
|
|
||
| "count - Map의 항목 개수" { | ||
| map.count() shouldBe 4 | ||
| emptyMap<String, Int>().count() shouldBe 0 | ||
| } | ||
|
|
||
| "reduce - Map.Entry의 누적 연산 (빈 Map에는 사용 불가)" { | ||
| val map: Map<String, Int> = mapOf("a" to 1, "b" to 2, "c" to 3) | ||
|
|
||
| val reducedEntry: Map.Entry<String, Int> = | ||
| map.entries.reduce { acc, entry -> | ||
| object : Map.Entry<String, Int> { | ||
| override val key: String = acc.key + entry.key | ||
| override val value: Int = acc.value + entry.value | ||
| } | ||
| } | ||
|
|
||
| reducedEntry.key shouldBe "abc" | ||
| reducedEntry.value shouldBe 6 | ||
| } | ||
|
|
||
| "fold - Map.Entry의 누적 연산 (초기값 사용)" { | ||
| val map: Map<String, Int> = mapOf("a" to 1, "b" to 2, "c" to 3) | ||
| val initial: Map.Entry<String, Int> = | ||
| object : Map.Entry<String, Int> { | ||
| override val key: String = "start " | ||
| override val value: Int = 100 | ||
| } | ||
|
|
||
| val foldedValue: Map.Entry<String, Int> = | ||
| map.entries.fold(initial) { acc, entry -> | ||
| object : Map.Entry<String, Int> { | ||
| override val key: String = acc.key + entry.key | ||
| override val value: Int = acc.value + entry.value | ||
| } | ||
| } | ||
|
|
||
| foldedValue.key shouldBe "start abc" | ||
| foldedValue.value shouldBe 106 | ||
| } | ||
| } | ||
| }) | ||
121 changes: 121 additions & 0 deletions
121
app/src/test/java/com/example/learningtest/collection/functional/CombinationTest.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| package com.example.learningtest.collection.functional | ||
|
|
||
| import io.kotest.core.spec.style.FreeSpec | ||
| import io.kotest.matchers.shouldBe | ||
|
|
||
| class CombinationTest : FreeSpec({ | ||
| "List Combination" - { | ||
| "plus (+) - 두 컬렉션을 이어붙이거나 요소 하나를 추가" { | ||
| val list1 = listOf(1, 3) | ||
| val list2 = listOf(2, 4) | ||
|
|
||
| list1 + list2 shouldBe listOf(1, 3, 2, 4) | ||
|
|
||
| list1 + 2 shouldBe listOf(1, 3, 2) | ||
| // (2 + list1) 처럼 (원소 + 리스트)는 불가능 | ||
| } | ||
|
|
||
| "minus (-) - 다른 컬렉션이나 특정 요소를 제거" { | ||
| val list1 = listOf(1, 2, 3, 2) | ||
| val list2 = listOf(1, 4) | ||
|
|
||
| list1 - list2 shouldBe listOf(2, 3, 2) | ||
| list1 - 1 shouldBe listOf(2, 3, 2) | ||
| } | ||
|
|
||
| "union - 두 컬렉션의 합집합 (중복 제거)" { | ||
| val list1 = listOf<Int>(1, 2, 3) | ||
| val list2 = listOf<Int>(3, 4, 5) | ||
|
|
||
| (list1 union list2) shouldBe setOf<Int>(1, 2, 3, 4, 5) | ||
| } | ||
|
|
||
| "intersect - 두 컬렉션의 교집합 (중복 제거)" { | ||
| val list1 = listOf(1, 2, 3, 4, 2) | ||
| val list2 = listOf(3, 4, 5, 4) | ||
|
|
||
| (list1 intersect list2) shouldBe setOf(3, 4) | ||
| } | ||
|
|
||
| "subtract - 첫 번째 컬렉션에서 두 번째 컬렉션의 요소를 제외한 차집합 (중복 제거)" { | ||
| val list1 = listOf<Int>(1, 2, 3, 4, 2) | ||
| val list2 = listOf<Int>(3, 4, 5, 4) | ||
|
|
||
| (list1 subtract list2) shouldBe setOf(1, 2) // list1 에서의 중복도 제거됨. | ||
| } | ||
| } | ||
|
|
||
| "Map Combination" - { | ||
| val map1 = mapOf("a" to 1, "b" to 2) | ||
| val map2 = mapOf("b" to 20, "c" to 30) | ||
|
|
||
| "plus (+) " { | ||
| map1 + map2 shouldBe mapOf("a" to 1, "b" to 20, "c" to 30) | ||
| } | ||
|
|
||
| "merge - 키 존재 시 값 결합, 키 부재 시 추가" { | ||
| val mutableMap = mutableMapOf("a" to 1, "b" to 2) | ||
|
|
||
| mutableMap.merge("c", 10) { oldValue, newValue -> | ||
| oldValue + newValue | ||
| } | ||
|
|
||
| mutableMap shouldBe mapOf("a" to 1, "b" to 2, "c" to 10) | ||
|
|
||
| mutableMap.merge("a", 10) { oldValue, newValue -> | ||
| oldValue + newValue | ||
| } | ||
|
|
||
| mutableMap shouldBe mapOf("a" to 11, "b" to 2, "c" to 10) | ||
| } | ||
|
|
||
| "entries, keys, values 를 이용한 union, intersect, subtract (기존 테스트 유지)" - { | ||
| "entries 를 이용한 union (결과 Set)" { | ||
| val unionResult: Set<Map.Entry<String, Int>> = map1.entries union map2.entries | ||
| unionResult shouldBe | ||
| setOf( | ||
| mapOf("a" to 1).entries.first(), | ||
| mapOf("b" to 2).entries.first(), | ||
| mapOf("b" to 20).entries.first(), | ||
| mapOf("c" to 30).entries.first(), | ||
| ) | ||
| } | ||
|
|
||
| "entries 를 이용한 intersect (결과 Set)" { | ||
| val intersectResult: Set<Map.Entry<String, Int>> = | ||
| map1.entries intersect map2.entries | ||
|
|
||
| intersectResult shouldBe emptySet() | ||
|
|
||
| val map3 = mapOf("a" to 1, "b" to 2) | ||
| val map4 = mapOf("a" to 1, "b" to 4) | ||
|
|
||
| val intersectResult2: Set<Map.Entry<String, Int>> = | ||
| map3.entries intersect map4.entries | ||
|
|
||
| intersectResult2 shouldBe setOf(mapOf("a" to 1).entries.first()) | ||
| } | ||
|
|
||
| "keys 를 이용한 union (결과 Set)" { | ||
| val unionResult: Set<String> = map1.keys union map2.keys | ||
| unionResult shouldBe setOf("a", "b", "c") | ||
| } | ||
|
|
||
| "keys 를 이용한 intersect (결과 Set)" { | ||
| val intersectResult: Set<String> = map1.keys intersect map2.keys | ||
| intersectResult shouldBe setOf("b") | ||
| } | ||
|
|
||
| "keys 를 이용한 subtract (결과 Set)" { | ||
| val subtractResult: Set<String> = map1.keys subtract map2.keys | ||
| subtractResult shouldBe setOf("a") | ||
| } | ||
|
|
||
| "values 를 이용한 조합 union (결과 Set)" { | ||
| val unionResult: Set<Int> = map1.values union map2.values | ||
|
|
||
| unionResult shouldBe setOf(1, 2, 20, 30) | ||
| } | ||
| } | ||
| } | ||
| }) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.