컬렉션의 일부 가져오기(Retrieve collection parts)
컬렉션의 일부 가져오기(Retrieve collection parts)
Kotlin 표준 라이브러리에는 컬렉션의 일부를 가져오는 확장 함수가 있어요. 이 함수들은 결과 컬렉션에 들어갈 요소를 고르는 다양한 방법을 제공합니다 — 위치를 명시적으로 나열하거나, 결과 크기를 지정하는 방법 같은 것들이죠.
출처: Kotlin 공식 문서
본문
Slice
slice()는 주어진 인덱스에 해당하는 컬렉션 요소들의 리스트를 반환해요. 인덱스는 범위로 넘길 수도 있고, 정수 값의 컬렉션으로 넘길 수도 있습니다:
fun main() {
//sampleStart
val numbers = listOf("one", "two", "three", "four", "five", "six")
println(numbers.slice(1..3))
println(numbers.slice(0..4 step 2))
println(numbers.slice(setOf(3, 5, 0)))
//sampleEnd
}
Take와 Drop
첫 번째 요소부터 시작해서 지정한 개수만큼 가져오려면 take() 함수를 써요. 마지막 요소들을 가져올 때는 takeLast()를 쓰고요. 컬렉션 크기보다 큰 숫자로 호출하면 두 함수 모두 컬렉션 전체를 반환합니다. 첫 또는 마지막 요소를 지정한 개수만큼 제외한 나머지를 모두 가져오려면 각각 drop()과 dropLast()를 호출하면 돼요:
fun main() {
//sampleStart
val numbers = listOf("one", "two", "three", "four", "five", "six")
println(numbers.take(3))
println(numbers.takeLast(3))
println(numbers.drop(1))
println(numbers.dropLast(5))
//sampleEnd
}
predicate(조건자)로 가져오거나 버릴 요소의 개수를 정할 수도 있어요. 위에서 설명한 함수들과 비슷한 네 가지 함수가 있습니다:
takeWhile()은take()에 predicate를 더한 것이에요. predicate를 만족하지 않는 첫 요소까지만 가져오고, 그 요소는 제외하죠. 컬렉션의 첫 요소가 predicate를 만족하지 않으면 결과는 비어 있어요.takeLastWhile()은takeLast()와 비슷해요. 컬렉션의 끝에서부터 predicate를 만족하는 요소들의 범위를 가져옵니다. 그 범위의 첫 요소는 predicate를 만족하지 않는 마지막 요소 바로 다음의 요소예요. 컬렉션의 마지막 요소가 predicate를 만족하지 않으면 결과는 비어 있죠.dropWhile()은 같은 predicate를 쓰는takeWhile()의 반대예요. predicate를 만족하지 않는 첫 요소부터 끝까지 반환합니다.dropLastWhile()은 같은 predicate를 쓰는takeLastWhile()의 반대예요. 처음부터 predicate를 만족하지 않는 마지막 요소까지 반환해요.
fun main() {
//sampleStart
val numbers = listOf("one", "two", "three", "four", "five", "six")
println(numbers.takeWhile { !it.startsWith('f') })
println(numbers.takeLastWhile { it != "three" })
println(numbers.dropWhile { it.length == 3 })
println(numbers.dropLastWhile { it.contains('i') })
//sampleEnd
}
Chunked
컬렉션을 주어진 크기의 조각으로 나누려면 chunked() 함수를 써요. chunked()는 인자를 하나 받는데 바로 조각(chunk)의 크기이고, 주어진 크기의 List의 List를 반환합니다. 첫 번째 조각은 첫 요소부터 시작해서 size개 요소를 담고, 두 번째 조각은 그다음 size개 요소를 담는 식이죠. 마지막 조각은 크기가 더 작을 수 있어요:
fun main() {
//sampleStart
val numbers = (0..13).toList()
println(numbers.chunked(3))
//sampleEnd
}
반환된 조각들에 변환을 바로 적용할 수도 있어요. chunked()를 호출할 때 변환을 람다 함수로 넘기면 됩니다. 람다의 인자는 컬렉션의 조각 하나예요. 변환과 함께 chunked()를 호출하면 조각들은 그 람다 안에서 바로 소비되어야 하는 짧게 사는 List라는 점을 기억하세요:
fun main() {
//sampleStart
val numbers = (0..13).toList()
println(numbers.chunked(3) { it.sum() }) // `it` is a chunk of the original collection
//sampleEnd
}
Windowed
주어진 크기의 컬렉션 요소 범위를 모두 가져올 수도 있어요. 그 역할을 하는 함수가 windowed()입니다: 주어진 크기의 슬라이딩 윈도우로 컬렉션을 바라봤을 때 보이는 요소 범위들의 리스트를 반환하죠. chunked()와 달리 windowed()는 각 컬렉션 요소마다 시작하는 요소 범위(윈도우)를 반환해요. 모든 윈도우는 하나의 List의 요소로 반환됩니다:
fun main() {
//sampleStart
val numbers = listOf("one", "two", "three", "four", "five")
println(numbers.windowed(3))
//sampleEnd
}
windowed()는 기본값이 있는 파라미터 덕분에 더 유연하게 쓸 수 있습니다:
step은 인접한 두 윈도우의 첫 요소 사이의 거리를 정해요. 기본값은 1이라서 모든 요소에서 시작하는 윈도우가 결과에 들어오죠. step을 2로 올리면 첫 번째, 세 번째처럼 홀수 번째 요소에서만 시작하는 윈도우를 받게 됩니다.partialWindows는 컬렉션 끝의 요소들에서 시작하는 더 작은 크기의 윈도우를 포함해요. 예를 들어 3개 요소짜리 윈도우를 요청하면 마지막 두 요소로는 윈도우를 만들 수 없는데,partialWindows를 켜면 크기 2와 1인 리스트 두 개가 더 포함되는 식이죠.
마지막으로, 반환된 범위에 변환을 바로 적용할 수도 있어요. windowed()를 호출할 때 변환을 람다 함수로 넘기면 됩니다:
fun main() {
//sampleStart
val numbers = (1..10).toList()
println(numbers.windowed(3, step = 2, partialWindows = true))
println(numbers.windowed(3) { it.sum() })
//sampleEnd
}
2개 요소짜리 윈도우를 만들기 위한 별도 함수도 있어요 — zipWithNext()입니다. 이 함수는 수신(receiver) 컬렉션의 인접한 요소들의 쌍을 만들어요. zipWithNext()는 컬렉션을 쌍으로 나누는 게 아니라, 마지막 요소를 제외한 각 요소에 대해 Pair를 만든다는 점을 기억하세요. 그래서 [1, 2, 3, 4]에 대한 결과는 [[1, 2], [2, 3], [3, 4]]이지 [[1, 2], [3, 4]]가 아니에요. zipWithNext()는 변환 함수와 함께 호출할 수도 있고요, 그 경우 수신 컬렉션의 두 요소를 인자로 받아야 합니다:
fun main() {
//sampleStart
val numbers = listOf("one", "two", "three", "four", "five")
println(numbers.zipWithNext())
println(numbers.zipWithNext() { s1, s2 -> s1.length > s2.length})
//sampleEnd
}