Skip to content
Merged
Show file tree
Hide file tree
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 Apr 15, 2025
e082580
test(MappingTest): mapping api for Kotlin List
sh1mj1 Apr 15, 2025
5bdbdc0
test(MappingTest): add mapTo type methods
sh1mj1 Apr 15, 2025
53aba00
test(MappingTest): add flatMapTo type methods
sh1mj1 Apr 15, 2025
2e866eb
test(MappingTest): add associateTo type methods
sh1mj1 Apr 15, 2025
b5ba82c
test(FlatteningTest): add flattening type methods test
sh1mj1 Apr 15, 2025
006edd0
test(FilteringTest): add filtering and collection manipulation method…
sh1mj1 Apr 17, 2025
7b8589b
test(FilteringTest): add tests for Map type's filtering methods
sh1mj1 Apr 17, 2025
b0ecd5c
test(FlatteningTest): update test descriptions for clarity on flatten…
sh1mj1 Apr 17, 2025
c7da45b
test(FlatteningTest): add comprehensive tests for flattening methods …
sh1mj1 Apr 17, 2025
5f06438
test(FilteringTest, FlatteningTest, MappingTest): update variable dec…
sh1mj1 Apr 17, 2025
8233cc3
chore(ListFunctionalApiTest.kt): remove
sh1mj1 Apr 17, 2025
e8b0343
test(FlatteningTest): improve test description and update map flatten…
sh1mj1 Apr 18, 2025
3d766d4
test(MappingTest): refactor mapIndexedNotNull logic for improved read…
sh1mj1 Apr 18, 2025
2fad10d
test(SearchingTest): add comprehensive tests for list searching methods
sh1mj1 Apr 18, 2025
aeb0a7f
test(SearchingTest): add more indexOf tests
sh1mj1 Apr 18, 2025
6d4fd4c
test(SearchingTest): add more indexOf tests
sh1mj1 Apr 18, 2025
4f65083
test(SortingTest): add comprehensive tests for list sorting methods
sh1mj1 Apr 22, 2025
f8fd893
test(SortingTest): add tests for sortedWith functionality with variou…
sh1mj1 Apr 22, 2025
7bc7c00
test(SortingTest): sortedWith 테스트 추가
sh1mj1 May 2, 2025
e9ad6d6
test(SortingTest): 기존 리스트 정렬 메서드에 대한 포괄적인 테스트 추가
sh1mj1 May 2, 2025
16d4bfb
test(SortingTest): map 타입의 정렬
sh1mj1 May 2, 2025
6a79e5e
feat(AggregationTest): aggregation api
sh1mj1 May 2, 2025
304000f
refactor(AggregationTest): rename variables
sh1mj1 May 2, 2025
50269fe
refactor(AggregationTest): specify some variable's type
sh1mj1 May 2, 2025
e9a69cc
fix(AggregationTest): 테스트 이름 수정, runningFold 실패하는 테스트 수정
sh1mj1 May 2, 2025
7c8b0ca
test(GroupingTest): 리스트 그루핑 메서드 테스트
sh1mj1 May 2, 2025
19bec61
test(TransformationTest): List 변형 메서드
sh1mj1 May 2, 2025
f5a9d7d
test(TransformationTest): Set 변형 메서드
sh1mj1 May 2, 2025
3a74c0e
test(TransformationTest): Map transformation 메서드
sh1mj1 May 2, 2025
84c7852
test(CombinationTest): 컬렉션 조합 연산에 대한 테스트 추가
sh1mj1 May 5, 2025
b4ae358
test(ControlFlowExtensionTest): Kotlin 스코프 함수 및 조건부 함수 학습
sh1mj1 May 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
}
})
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)
}
}
}
})
Loading