Kotlin Koans Workshop - II. collections

이미지 출처: https://kotlinlang.org/assets/images/open-graph/kotlin_250x250.png

테스트케이스로 코틀린 문법익히기

Chapter2 - Kotlin Collection 표준라이브러리 이용하기

Kotlin Github repository의 kotlin-koans 프로젝트를 기반으로 포스팅 되었음을 밝힘니다.
출처: https://github.com/Kotlin/kotlin-koans

목차

예제용 Shop클래스 목차 ⇈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package ii_collections

data class Shop(val name: String, val customers: List<Customer>)

data class Customer(val name: String, val city: City, val orders: List<Order>) {
override fun toString() = "$name from ${city.name}"
}

data class Order(val products: List<Product>, val isDelivered: Boolean)

data class Product(val name: String, val price: Double) {
override fun toString() = "'$name' for $price"
}

data class City(val name: String) {
override fun toString() = name
}

예제용 TestShop클래스 목차 ⇈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package ii_collections.data

import ii_collections.*

fun main(arge: Array<String>) {
val a: MutableList<Int>? = mutableListOf(1, 2, 3)
a?.run{ add(7)}
// println(a.fold(1, { price, it -> price * it}))
}

//products
val idea = Product("IntelliJ IDEA Ultimate", 199.0)
val reSharper = Product("ReSharper", 149.0)
val dotTrace = Product("DotTrace", 159.0)
val dotMemory = Product("DotTrace", 129.0)
val dotCover = Product("DotCover", 99.0)
val appCode = Product("AppCode", 99.0)
val phpStorm = Product("PhpStorm", 99.0)
val pyCharm = Product("PyCharm", 99.0)
val rubyMine = Product("RubyMine", 99.0)
val webStorm = Product("WebStorm", 49.0)
val teamCity = Product("TeamCity", 299.0)
val youTrack = Product("YouTrack", 500.0)

//customers
val lucas = "Lucas"
val cooper = "Cooper"
val nathan = "Nathan"
val reka = "Reka"
val bajram = "Bajram"
val asuka = "Asuka"
val riku = "Riku"

//cities
val Canberra = City("Canberra")
val Vancouver = City("Vancouver")
val Budapest = City("Budapest")
val Ankara = City("Ankara")
val Tokyo = City("Tokyo")

fun customer(name: String, city: City, vararg orders: Order) = Customer(name, city, orders.toList())
fun order(vararg products: Product, isDelivered: Boolean = true) = Order(products.toList(), isDelivered)
fun shop(name: String, vararg customers: Customer) = Shop(name, customers.toList())

val shop = shop("jb test shop",
customer(lucas, Canberra,
order(reSharper),
order(reSharper, dotMemory, dotTrace)
),
customer(cooper, Canberra),
customer(nathan, Vancouver,
order(rubyMine, webStorm)
),
customer(reka, Budapest,
order(idea, isDelivered = false),
order(idea, isDelivered = false),
order(idea)
),
customer(bajram, Ankara,
order(reSharper)
),
customer(asuka, Tokyo,
order(idea)
),
customer(riku, Tokyo,
order(phpStorm, phpStorm),
order(phpStorm)
)

)

val customers: Map<String, Customer> = shop.customers.fold(hashMapOf<String, Customer>(), {
map, customer ->
map[customer.name] = customer
map
})

val orderedProducts = setOf(idea, reSharper, dotTrace, dotMemory, rubyMine, webStorm, phpStorm)

val sortedCustomers = listOf(cooper, nathan, bajram, asuka, lucas, riku, reka).map { customers[it] }

val groupedByCities = mapOf(
Canberra to listOf(lucas, cooper),
Vancouver to listOf(nathan),
Budapest to listOf(reka),
Ankara to listOf(bajram),
Tokyo to listOf(asuka, riku)
).mapValues { it.value.map { name -> customers[name] } }

코틀린 컬렉션 toSet 목차 ⇈

src package

1
2
3
4
5
6
7
// Shop 클래스에 getSetOfCustomers() 함수를 정의합니다.
fun Shop.getSetOfCustomers(): Set<Customer> {
// Return a set containing all the customers of this shop
// toSet 함수는 컬렉션에 포함된 엘리먼트를 Set객체에 담아서 반환합니다.
// 모든 엘리먼트는 원본 Iteration의 순서를 유지합니다.
return this.customers.toSet()
}

test package

1
2
3
@Test fun testSetOfCustomers() {
assertEquals(customers.values.toSet(), shop.getSetOfCustomers())
}

코틀린 컬렉션 map, filter 목차 ⇈

src package

1
2
3
4
5
6
7
8
9
10
11
12
13
fun Shop.getCitiesCustomersAreFrom(): Set<City> {
// Return the set of cities the customers are from
// map 함수는 지정된 변환 함수를 원본 컬렉션의 각 요소에 적용하고 결과값을 반환합니다.
// customers.map { element -> element.city }
// element 네임을 지정하지 않아도 it 키워드로 element에 접근이 가능합니다.
return customers.map { it.city }.toSet()
}

fun Shop.getCustomersFrom(city: City): List<Customer> {
// Return a list of the customers who live in the given city
// filter 함수는 지정된 조건과 일치하는 요소의 리스트를 반환합니다.
return customers.filter { it.city == city }
}

test package

1
2
3
@Test fun testCustomersFromCity() {
assertEquals(listOf(customers[lucas], customers[cooper]), shop.getCustomersFrom(Canberra))
}

코틀린 컬렉션 any, all, count, firstOrNull 목차 ⇈

src package

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
fun Customer.isFrom(city: City): Boolean {
// Return true if the customer is from the given city
return this.city == city
}

fun Shop.checkAllCustomersAreFrom(city: City): Boolean {
// Return true if all customers are from the given city
// 컬렉션의 모든 element가 주어진 조건과 일치하면 true를 반환하고 그렇지 않으면 false를 반환합니다.
return customers.all { it.isFrom(city)}
}

fun Shop.hasCustomerFrom(city: City): Boolean {
// Return true if there is at least one customer from the given city
// 컬렉션의 element중 주어진 조건과 일치하는 element가 1개 이상이면 true를 반환하고 그렇지 않으면 false를 반환합니다.
return customers.any { it.isFrom(city) }
}

fun Shop.countCustomersFrom(city: City): Int {
// Return the number of customers from the given city
// 컬렉션의 element중 주어진 조건과 일치하는 element의 수를 반환합니다.
return customers.count { it.isFrom(city) }
}

fun Shop.findFirstCustomerFrom(city: City): Customer? {
// Return the first customer who lives in the given city, or null if there is none
// 컬렉션의 element중 주어진 조건과 일치하는 첫번째 element를 반환합니다.
// 일치하는 element가 없으면 null를 반환합니다.
return customers.firstOrNull { it.isFrom(city) }
}

test package

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Test fun testCustomerIsFromCity() {
assertTrue(customers[lucas]!!.isFrom(Canberra))
assertFalse(customers[lucas]!!.isFrom(Budapest))
}

@Test fun testAllCustomersAreFromCity() {
assertFalse(shop.checkAllCustomersAreFrom(Canberra))
}

@Test fun testAnyCustomerIsFromCity() {
assertTrue(shop.hasCustomerFrom(Canberra))
}

@Test fun testCountCustomersFromCity() {
assertEquals(2, shop.countCustomersFrom(Canberra))
}

@Test fun testFirstCustomerFromCity() {
assertEquals(customers[lucas], shop.findFirstCustomerFrom(Canberra))
assertEquals(null, shop.findFirstCustomerFrom(City("Chicago")))
}

코틀린 컬렉션 flatMap 목차 ⇈

src package

1
2
3
4
5
6
7
8
9
10
11
// flatMap 함수는 원본 콜렉션의 각 요소에서 transform function이 실행된 결과로 얻은 모든 요소의 단일 목록을 리턴합니다.
val Customer.orderedProducts: Set<Product> get() {
// Return all products this customer has ordered
// 아래와 같이 flatMap을 이용하면 List<Order> 객체를 순회하며 Order.products를 모두 포함한 단일컬렉션을 반환할 수 있습니다.
return this.orders.flatMap { it.products }.toSet()
}

val Shop.allOrderedProducts: Set<Product> get() {
// Return all products that were ordered by at least one customer
return customers.flatMap { it.orderedProducts }.toSet()
}

test package

1
2
3
4
5
6
7
@Test fun testGetOrderedProductsSet() {
assertEquals(setOf(idea), customers[reka]!!.orderedProducts)
}

@Test fun testGetAllOrderedProducts() {
assertEquals(orderedProducts, shop.allOrderedProducts)
}

코틀린 컬렉션 maxBy 목차 ⇈

src package

1
2
3
4
5
6
7
8
9
10
11
fun Shop.getCustomerWithMaximumNumberOfOrders(): Customer? {
// Return a customer whose order count is the highest among all customers
// maxBy 함수는 주어진 조건의 값중 가장 큰값을 가지고 있는 element를 반환합니다.
// 만약 일치하는 값이 없으면 null값을 반환합니다.
return customers.maxBy { it.orders.size }
}

fun Customer.getMostExpensiveOrderedProduct(): Product? {
// Return the most expensive product which has been ordered
return orderedProducts.maxBy { it.price }
}

test package

1
2
3
4
5
6
7
@Test fun testCustomerWithMaximumNumberOfOrders() {
assertEquals(customers[reka], shop.getCustomerWithMaximumNumberOfOrders())
}

@Test fun testTheMostExpensiveOrderedProduct() {
assertEquals(rubyMine, customers[nathan]!!.getMostExpensiveOrderedProduct())
}

코틀린 컬렉션 sortBy 목차 ⇈

src package

1
2
3
4
5
fun Shop.getCustomersSortedByNumberOfOrders(): List<Customer> {
// Return a list of customers, sorted by the ascending number of orders they made
// sortedBy 함수는 주어진 조건을 기준으로 element를 정렬한 결과값을 반환합니다.
return customers.sortedBy { it.orders.size }
}

test package

1
2
3
@Test fun testGetCustomersSortedByNumberOfOrders() {
assertEquals(sortedCustomers, shop.getCustomersSortedByNumberOfOrders())
}

코틀린 컬렉션 sumByDouble 목차 ⇈

src package

1
2
3
4
5
6
fun Customer.getTotalOrderPrice(): Double {
// Return the sum of prices of all products that a customer has ordered.
// Note: a customer may order the same product for several times.
// sumByDouble 함수는 배열을 순회하며 선언된 속성값을 모두 합산하고 그 결과값을 반환합니다.
return orders.flatMap { it.products }.sumByDouble { it.price }
}

test package

1
2
3
4
5
6
7
@Test fun testGetTotalOrderPrice() {
assertEquals(148.0, customers[nathan]!!.getTotalOrderPrice(), 0.001)
}

@Test fun testTotalPriceForRepeatedProducts() {
assertEquals(586.0, customers[lucas]!!.getTotalOrderPrice(), 0.001)
}

코틀린 컬렉션 groupBy 목차 ⇈

src package

1
2
3
4
fun Shop.groupCustomersByCity(): Map<City, List<Customer>> {
// groupBy 함수는 배열 element의 키값(toString())을 기준으로 목록을 그룹화 하여 Map 객체를 반환합니다.
return customers.groupBy { it.city }
}

test package

1
2
3
@Test fun testGroupCustomersByCity() {
assertEquals(groupedByCities, shop.groupCustomersByCity())
}

코틀린 컬렉션 partition 목차 ⇈

src package

1
2
3
4
5
6
7
8
fun Shop.getCustomersWithMoreUndeliveredOrdersThanDelivered(): Set<Customer> {
// Return customers who have more undelivered orders than delivered
return customers.filter {
// partition 함수는 주어진 조건을 기준으로 true값은 첫번째 목록에, false값은 두번쨰 목록에 반환합니다.
val (delivered, undelivered) = it.orders.partition { it.isDelivered }
undelivered.size > delivered.size
}.toSet()
}

test package

1
2
3
@Test fun testGetCustomersWhoHaveMoreUndeliveredOrdersThanDelivered() {
assertEquals(setOf(customers[reka]), shop.getCustomersWithMoreUndeliveredOrdersThanDelivered())
}

코틀린 컬렉션 fold 목차 ⇈

src package

1
2
3
4
5
6
7
fun Shop.getSetOfProductsOrderedByEveryCustomer(): Set<Product> {
// Return the set of products ordered by every customer
return customers.fold(allOrderedProducts, {
orderedByAll, customer ->
orderedByAll.intersect(customer.orderedProducts)
})
}

test package

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test fun testGetProductsOrderedByAllCustomers() {
val testShop = shop("test shop for 'fold'",
customer(lucas, Canberra,
order(idea),
order(webStorm)
),
customer(reka, Budapest,
order(idea),
order(youTrack)
)
)
assertEquals(setOf(idea), testShop.getSetOfProductsOrderedByEveryCustomer())
}

코틀린 컬렉션 2개이상의 함수를 연속으로 호출하기 목차 ⇈

src package

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun Shop.getCustomersWhoOrderedProduct(product: Product): Set<Customer> {
// Return the set of customers who ordered the specified product
return customers.filter { it.orderedProducts.contains(product) }.toSet()
}

fun Customer.getMostExpensiveDeliveredProduct(): Product? {
// Return the most expensive product among all delivered products
// (use the Order.isDelivered flag)
return orders.filter { it.isDelivered }.flatMap { it.products }.maxBy { it.price }
}

fun Shop.getNumberOfTimesProductWasOrdered(product: Product): Int {
// Return the number of times the given product was ordered.
// Note: a customer may order the same product for several times.
return customers.flatMap { it.orders }.flatMap { it.products }.count {it == product}
}

test package

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Test fun testGetCustomersWhoOrderedProduct() {
assertEquals(setOf(customers[reka], customers[asuka]), shop.getCustomersWhoOrderedProduct(idea))
}

@Test fun testMostExpensiveDeliveredProduct() {
val testShop = shop("test shop for 'most expensive delivered product'",
customer(lucas, Canberra,
order(isDelivered = false, products = idea),
order(reSharper)
)
)
assertEquals(reSharper, testShop.customers[0].getMostExpensiveDeliveredProduct())
}

@Test fun testNumberOfTimesEachProductWasOrdered() {
assertEquals(4, shop.getNumberOfTimesProductWasOrdered(idea))
}

@Test fun testNumberOfTimesEachProductWasOrderedForRepeatedProduct() {
assertEquals("A customer may order a product for several times",
3, shop.getNumberOfTimesProductWasOrdered(reSharper))
}

@Test fun testNumberOfTimesEachProductWasOrderedForRepeatedInOrderProduct() {
assertEquals("An order may contain a particular product more than once",
3, shop.getNumberOfTimesProductWasOrdered(phpStorm))
}

코틀린 표준라이브러리를 이용하여 Java코드를 Kotlin코드로 변경하기 목차 ⇈

src package(Java)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public Collection<String> doSomethingStrangeWithCollection(Collection<String> collection) {
Map<Integer, List<String>> groupsByLength = Maps.newHashMap();
for (String s : collection) {
List<String> strings = groupsByLength.get(s.length());
if (strings == null) {
strings = Lists.newArrayList();
groupsByLength.put(s.length(), strings);
}
strings.add(s);
}

int maximumSizeOfGroup = 0;
for (List<String> group : groupsByLength.values()) {
if (group.size() > maximumSizeOfGroup) {
maximumSizeOfGroup = group.size();
}
}

for (List<String> group : groupsByLength.values()) {
if (group.size() == maximumSizeOfGroup) {
return group;
}
}
return null;
}

src package(Kotlin)

1
2
3
4
5
fun doSomethingStrangeWithCollection(collection: Collection<String>): Collection<String>? {
val groupsByLength = collection.groupBy { s -> s.length }

return groupsByLength.values.maxBy { group -> group.size }
}

test package

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Test fun testCollectionOfOneElement() {
doTest(listOf("a"), listOf("a"))
}

@Test fun testEmptyCollection() {
doTest(null, listOf())
}

@Test fun testSimpleCollection() {
doTest(listOf("a", "c"), listOf("a", "bb", "c"))
}

@Test fun testCollectionWithEmptyStrings() {
doTest(listOf("", "", "", ""), listOf("", "", "", "", "a", "bb", "ccc", "dddd"))
}

@Test fun testCollectionWithTwoGroupsOfMaximalSize() {
doTest(listOf("a", "c"), listOf("a", "bb", "c", "dd"))
}

private fun doTest(expected: Collection<String>?, argument: Collection<String>) {
assertEquals("The function 'doSomethingStrangeWithCollection' should do at least something with a collection:", expected, doSomethingStrangeWithCollection(argument))
}

Table of contents


Open source project on GitHub


Jupyter/IPython Notebook


Open API


NoSQL


Jekyll


Gradle


Kotlin


C++

  • C++ keywords
  • 윤성우 열혈강의 C++ 프로그래밍 예제코드 [펼치기]
    1. C언어 기반의 C++ 1
      • printf와 scanf를 대신하는 입출력 방식
      • 함수 오버로딩(Function Overloading)
      • 매개변수의 디폴트 값(Default Value)
      • 인라인 함수(Inline)함수
      • 이름공간(namespace)에 대한 소개
    2. C언어 기반의 C++ 2
      • 새로운 자료형 bool
      • 참조자(Reference)의 이해
      • 참조자(Reference)와 함수
      • maloc & free를 대신하는 new & delete
      • C++에서 C언어의 표준함수 호출하기
    3. 클래스의 기본
      • C++에서의 구조체
      • 클래스(Class)와 객체(Object)
      • 객체지향 프로그래밍의 이해
    4. 클래스의 완성
      • 정보은닉(Information Hiding)
      • 캡슐화(Encapsulation)
      • 생성자(Constructor)와 소멸자(Destructor)
      • 클래스와 배열 그리고 this 포인터
    5. 복사 생성자
      • ‘복사 생성자’ 와의 첫 만남
      • ‘깊은 복사’와 ‘얕은 복사’
      • 복사 생성자의 호출시점
    6. friend와 static 그리고 const
      • const와 관련해서 아직 못다한 이야기
      • 클래스와 함수에 대한 friend 선언
      • C++에서의 static
    7. 상속(Inheritance)의 이해
      • 상속에 들어가기에 앞서
      • 상속의 문법적인 이해
      • protected 선언과 세 가지 형태의 상속
      • 상속을 위한 조건
    8. 상속과 다형성
      • 객체 포인터의 참조관계
      • 가상함수(Vitual Function)
      • 가상 소멸자와 참조자의 참조 가능성
    9. 가상(Virtual)의 원리와 다중상속
      • 멤버함수와 가상함수의 동작원리
      • 다중상속(Multiple Inheritance)에 대한 이해
    10. 연산자 오버로딩 1
      • 연산자 오버로딩의 이해와 유형
      • 단항 연산자의 오버로딩의
      • 교환법칙 문제의 해결
      • cout, cin 그리고 endl의 정체
    11. 연산자 오버로딩 2
      • 반드시 해야 하는 대입 연산자의 오버로딩
      • 배열의 인덱스 연산자 오버로딩
      • 그 이외의 연산자 오버로딩
    12. String 클래스의 디자인
      • C++ 표준과 표즌 string 클래스
      • 문자열 처리 클래스의 정의
    13. 템플릿(Template) 1
      • 템플릿(Template)에 대한 이해와 함수 템플릿
      • 클래스 템플릿(Class Temlpate)
    14. 템플릿(Template) 2
      • Chapter 13에서 공부한 내용의 확장
      • 클래스 템플릿의 특수화(Class Temlpate Specialization)
      • 템플릿의 인자
      • 템플릿과 static
    15. 예외처리(Exception Handling)
      • 예외상황과 예외처리의 이해
      • C++의 예외처리 메커니즘
      • Stack Unwinding(스택 풀기)
      • 예외상황을 표현하는 예외 클래스의 설계
      • 예외처리와 관련된 또 다른 특성들
    16. C++의 형 변환 연산자
      • C++에서의 형 변환 연산

ETC