From 008a8059cfe5de99fbe74a9ef3c44c583ff21264 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Sat, 7 Nov 2015 17:55:33 +0300 Subject: [PATCH] Introduce filterIndexed #KT-9502 Fixed --- libraries/stdlib/src/generated/_Arrays.kt | 189 ++++++++++++++++-- .../stdlib/src/generated/_Collections.kt | 21 +- libraries/stdlib/src/generated/_Sequences.kt | 22 +- libraries/stdlib/src/generated/_Strings.kt | 26 ++- .../stdlib/test/collections/ArraysTest.kt | 9 + .../stdlib/test/collections/IterableTests.kt | 5 + .../stdlib/test/collections/SequenceTest.kt | 4 + libraries/stdlib/test/text/StringTest.kt | 9 + .../src/templates/Filtering.kt | 52 ++++- 9 files changed, 310 insertions(+), 27 deletions(-) diff --git a/libraries/stdlib/src/generated/_Arrays.kt b/libraries/stdlib/src/generated/_Arrays.kt index 2110e1de8eb..13cf0acc89f 100644 --- a/libraries/stdlib/src/generated/_Arrays.kt +++ b/libraries/stdlib/src/generated/_Arrays.kt @@ -2986,68 +2986,221 @@ public inline fun ShortArray.dropWhile(predicate: (Short) -> Boolean): List Array.filter(predicate: (T) -> Boolean): List { return filterTo(ArrayList(), predicate) } /** - * Returns a list containing all elements matching the given [predicate]. + * Returns a list containing only elements matching the given [predicate]. */ public inline fun BooleanArray.filter(predicate: (Boolean) -> Boolean): List { return filterTo(ArrayList(), predicate) } /** - * Returns a list containing all elements matching the given [predicate]. + * Returns a list containing only elements matching the given [predicate]. */ public inline fun ByteArray.filter(predicate: (Byte) -> Boolean): List { return filterTo(ArrayList(), predicate) } /** - * Returns a list containing all elements matching the given [predicate]. + * Returns a list containing only elements matching the given [predicate]. */ public inline fun CharArray.filter(predicate: (Char) -> Boolean): List { return filterTo(ArrayList(), predicate) } /** - * Returns a list containing all elements matching the given [predicate]. + * Returns a list containing only elements matching the given [predicate]. */ public inline fun DoubleArray.filter(predicate: (Double) -> Boolean): List { return filterTo(ArrayList(), predicate) } /** - * Returns a list containing all elements matching the given [predicate]. + * Returns a list containing only elements matching the given [predicate]. */ public inline fun FloatArray.filter(predicate: (Float) -> Boolean): List { return filterTo(ArrayList(), predicate) } /** - * Returns a list containing all elements matching the given [predicate]. + * Returns a list containing only elements matching the given [predicate]. */ public inline fun IntArray.filter(predicate: (Int) -> Boolean): List { return filterTo(ArrayList(), predicate) } /** - * Returns a list containing all elements matching the given [predicate]. + * Returns a list containing only elements matching the given [predicate]. */ public inline fun LongArray.filter(predicate: (Long) -> Boolean): List { return filterTo(ArrayList(), predicate) } /** - * Returns a list containing all elements matching the given [predicate]. + * Returns a list containing only elements matching the given [predicate]. */ public inline fun ShortArray.filter(predicate: (Short) -> Boolean): List { return filterTo(ArrayList(), predicate) } +/** + * Returns a list containing only elements matching the given [predicate]. + */ +public inline fun Array.filterIndexed(predicate: (Int, T) -> Boolean): List { + return filterIndexedTo(ArrayList(), predicate) +} + +/** + * Returns a list containing only elements matching the given [predicate]. + */ +public inline fun BooleanArray.filterIndexed(predicate: (Int, Boolean) -> Boolean): List { + return filterIndexedTo(ArrayList(), predicate) +} + +/** + * Returns a list containing only elements matching the given [predicate]. + */ +public inline fun ByteArray.filterIndexed(predicate: (Int, Byte) -> Boolean): List { + return filterIndexedTo(ArrayList(), predicate) +} + +/** + * Returns a list containing only elements matching the given [predicate]. + */ +public inline fun CharArray.filterIndexed(predicate: (Int, Char) -> Boolean): List { + return filterIndexedTo(ArrayList(), predicate) +} + +/** + * Returns a list containing only elements matching the given [predicate]. + */ +public inline fun DoubleArray.filterIndexed(predicate: (Int, Double) -> Boolean): List { + return filterIndexedTo(ArrayList(), predicate) +} + +/** + * Returns a list containing only elements matching the given [predicate]. + */ +public inline fun FloatArray.filterIndexed(predicate: (Int, Float) -> Boolean): List { + return filterIndexedTo(ArrayList(), predicate) +} + +/** + * Returns a list containing only elements matching the given [predicate]. + */ +public inline fun IntArray.filterIndexed(predicate: (Int, Int) -> Boolean): List { + return filterIndexedTo(ArrayList(), predicate) +} + +/** + * Returns a list containing only elements matching the given [predicate]. + */ +public inline fun LongArray.filterIndexed(predicate: (Int, Long) -> Boolean): List { + return filterIndexedTo(ArrayList(), predicate) +} + +/** + * Returns a list containing only elements matching the given [predicate]. + */ +public inline fun ShortArray.filterIndexed(predicate: (Int, Short) -> Boolean): List { + return filterIndexedTo(ArrayList(), predicate) +} + +/** + * Appends all elements matching the given [predicate] to the given [destination]. + */ +public inline fun > Array.filterIndexedTo(destination: C, predicate: (Int, T) -> Boolean): C { + forEachIndexed { index, element -> + if (predicate(index, element)) destination.add(element) + } + return destination +} + +/** + * Appends all elements matching the given [predicate] to the given [destination]. + */ +public inline fun > BooleanArray.filterIndexedTo(destination: C, predicate: (Int, Boolean) -> Boolean): C { + forEachIndexed { index, element -> + if (predicate(index, element)) destination.add(element) + } + return destination +} + +/** + * Appends all elements matching the given [predicate] to the given [destination]. + */ +public inline fun > ByteArray.filterIndexedTo(destination: C, predicate: (Int, Byte) -> Boolean): C { + forEachIndexed { index, element -> + if (predicate(index, element)) destination.add(element) + } + return destination +} + +/** + * Appends all elements matching the given [predicate] to the given [destination]. + */ +public inline fun > CharArray.filterIndexedTo(destination: C, predicate: (Int, Char) -> Boolean): C { + forEachIndexed { index, element -> + if (predicate(index, element)) destination.add(element) + } + return destination +} + +/** + * Appends all elements matching the given [predicate] to the given [destination]. + */ +public inline fun > DoubleArray.filterIndexedTo(destination: C, predicate: (Int, Double) -> Boolean): C { + forEachIndexed { index, element -> + if (predicate(index, element)) destination.add(element) + } + return destination +} + +/** + * Appends all elements matching the given [predicate] to the given [destination]. + */ +public inline fun > FloatArray.filterIndexedTo(destination: C, predicate: (Int, Float) -> Boolean): C { + forEachIndexed { index, element -> + if (predicate(index, element)) destination.add(element) + } + return destination +} + +/** + * Appends all elements matching the given [predicate] to the given [destination]. + */ +public inline fun > IntArray.filterIndexedTo(destination: C, predicate: (Int, Int) -> Boolean): C { + forEachIndexed { index, element -> + if (predicate(index, element)) destination.add(element) + } + return destination +} + +/** + * Appends all elements matching the given [predicate] to the given [destination]. + */ +public inline fun > LongArray.filterIndexedTo(destination: C, predicate: (Int, Long) -> Boolean): C { + forEachIndexed { index, element -> + if (predicate(index, element)) destination.add(element) + } + return destination +} + +/** + * Appends all elements matching the given [predicate] to the given [destination]. + */ +public inline fun > ShortArray.filterIndexedTo(destination: C, predicate: (Int, Short) -> Boolean): C { + forEachIndexed { index, element -> + if (predicate(index, element)) destination.add(element) + } + return destination +} + /** * Returns a list containing all elements not matching the given [predicate]. */ @@ -3199,7 +3352,7 @@ public inline fun > ShortArray.filterNotTo(desti } /** - * Appends all elements matching the given [predicate] into the given [destination]. + * Appends all elements matching the given [predicate] to the given [destination]. */ public inline fun > Array.filterTo(destination: C, predicate: (T) -> Boolean): C { for (element in this) if (predicate(element)) destination.add(element) @@ -3207,7 +3360,7 @@ public inline fun > Array.filterTo(destina } /** - * Appends all elements matching the given [predicate] into the given [destination]. + * Appends all elements matching the given [predicate] to the given [destination]. */ public inline fun > BooleanArray.filterTo(destination: C, predicate: (Boolean) -> Boolean): C { for (element in this) if (predicate(element)) destination.add(element) @@ -3215,7 +3368,7 @@ public inline fun > BooleanArray.filterTo(dest } /** - * Appends all elements matching the given [predicate] into the given [destination]. + * Appends all elements matching the given [predicate] to the given [destination]. */ public inline fun > ByteArray.filterTo(destination: C, predicate: (Byte) -> Boolean): C { for (element in this) if (predicate(element)) destination.add(element) @@ -3223,7 +3376,7 @@ public inline fun > ByteArray.filterTo(destinatio } /** - * Appends all elements matching the given [predicate] into the given [destination]. + * Appends all elements matching the given [predicate] to the given [destination]. */ public inline fun > CharArray.filterTo(destination: C, predicate: (Char) -> Boolean): C { for (element in this) if (predicate(element)) destination.add(element) @@ -3231,7 +3384,7 @@ public inline fun > CharArray.filterTo(destinatio } /** - * Appends all elements matching the given [predicate] into the given [destination]. + * Appends all elements matching the given [predicate] to the given [destination]. */ public inline fun > DoubleArray.filterTo(destination: C, predicate: (Double) -> Boolean): C { for (element in this) if (predicate(element)) destination.add(element) @@ -3239,7 +3392,7 @@ public inline fun > DoubleArray.filterTo(destin } /** - * Appends all elements matching the given [predicate] into the given [destination]. + * Appends all elements matching the given [predicate] to the given [destination]. */ public inline fun > FloatArray.filterTo(destination: C, predicate: (Float) -> Boolean): C { for (element in this) if (predicate(element)) destination.add(element) @@ -3247,7 +3400,7 @@ public inline fun > FloatArray.filterTo(destinat } /** - * Appends all elements matching the given [predicate] into the given [destination]. + * Appends all elements matching the given [predicate] to the given [destination]. */ public inline fun > IntArray.filterTo(destination: C, predicate: (Int) -> Boolean): C { for (element in this) if (predicate(element)) destination.add(element) @@ -3255,7 +3408,7 @@ public inline fun > IntArray.filterTo(destination: } /** - * Appends all elements matching the given [predicate] into the given [destination]. + * Appends all elements matching the given [predicate] to the given [destination]. */ public inline fun > LongArray.filterTo(destination: C, predicate: (Long) -> Boolean): C { for (element in this) if (predicate(element)) destination.add(element) @@ -3263,7 +3416,7 @@ public inline fun > LongArray.filterTo(destinatio } /** - * Appends all elements matching the given [predicate] into the given [destination]. + * Appends all elements matching the given [predicate] to the given [destination]. */ public inline fun > ShortArray.filterTo(destination: C, predicate: (Short) -> Boolean): C { for (element in this) if (predicate(element)) destination.add(element) diff --git a/libraries/stdlib/src/generated/_Collections.kt b/libraries/stdlib/src/generated/_Collections.kt index 60100481844..5421f4544e6 100644 --- a/libraries/stdlib/src/generated/_Collections.kt +++ b/libraries/stdlib/src/generated/_Collections.kt @@ -668,12 +668,29 @@ public inline fun Iterable.dropWhile(predicate: (T) -> Boolean): List } /** - * Returns a list containing all elements matching the given [predicate]. + * Returns a list containing only elements matching the given [predicate]. */ public inline fun Iterable.filter(predicate: (T) -> Boolean): List { return filterTo(ArrayList(), predicate) } +/** + * Returns a list containing only elements matching the given [predicate]. + */ +public inline fun Iterable.filterIndexed(predicate: (Int, T) -> Boolean): List { + return filterIndexedTo(ArrayList(), predicate) +} + +/** + * Appends all elements matching the given [predicate] to the given [destination]. + */ +public inline fun > Iterable.filterIndexedTo(destination: C, predicate: (Int, T) -> Boolean): C { + forEachIndexed { index, element -> + if (predicate(index, element)) destination.add(element) + } + return destination +} + /** * Returns a list containing all elements not matching the given [predicate]. */ @@ -705,7 +722,7 @@ public inline fun > Iterable.filterNotTo(desti } /** - * Appends all elements matching the given [predicate] into the given [destination]. + * Appends all elements matching the given [predicate] to the given [destination]. */ public inline fun > Iterable.filterTo(destination: C, predicate: (T) -> Boolean): C { for (element in this) if (predicate(element)) destination.add(element) diff --git a/libraries/stdlib/src/generated/_Sequences.kt b/libraries/stdlib/src/generated/_Sequences.kt index 8096517d672..333a15b555f 100644 --- a/libraries/stdlib/src/generated/_Sequences.kt +++ b/libraries/stdlib/src/generated/_Sequences.kt @@ -356,12 +356,30 @@ public fun Sequence.dropWhile(predicate: (T) -> Boolean): Sequence { } /** - * Returns a sequence containing all elements matching the given [predicate]. + * Returns a sequence containing only elements matching the given [predicate]. */ public fun Sequence.filter(predicate: (T) -> Boolean): Sequence { return FilteringSequence(this, true, predicate) } +/** + * Returns a sequence containing only elements matching the given [predicate]. + */ +public fun Sequence.filterIndexed(predicate: (Int, T) -> Boolean): Sequence { + // TODO: Rewrite with generalized MapFilterIndexingSequence + return TransformingSequence(FilteringSequence(IndexingSequence(this), true, { predicate(it.index, it.value) }), { it.value }) +} + +/** + * Appends all elements matching the given [predicate] to the given [destination]. + */ +public inline fun > Sequence.filterIndexedTo(destination: C, predicate: (Int, T) -> Boolean): C { + forEachIndexed { index, element -> + if (predicate(index, element)) destination.add(element) + } + return destination +} + /** * Returns a sequence containing all elements not matching the given [predicate]. */ @@ -393,7 +411,7 @@ public inline fun > Sequence.filterNotTo(desti } /** - * Appends all elements matching the given [predicate] into the given [destination]. + * Appends all elements matching the given [predicate] to the given [destination]. */ public inline fun > Sequence.filterTo(destination: C, predicate: (T) -> Boolean): C { for (element in this) if (predicate(element)) destination.add(element) diff --git a/libraries/stdlib/src/generated/_Strings.kt b/libraries/stdlib/src/generated/_Strings.kt index b1e840aa995..2851d2f7b3f 100644 --- a/libraries/stdlib/src/generated/_Strings.kt +++ b/libraries/stdlib/src/generated/_Strings.kt @@ -517,6 +517,30 @@ public inline fun String.filter(predicate: (Char) -> Boolean): String { return filterTo(StringBuilder(), predicate).toString() } +/** + * Returns a char sequence containing only those characters from the original char sequence that match the given [predicate]. + */ +public inline fun CharSequence.filterIndexed(predicate: (Int, Char) -> Boolean): CharSequence { + return filterIndexedTo(StringBuilder(), predicate) +} + +/** + * Returns a string containing only those characters from the original string that match the given [predicate]. + */ +public inline fun String.filterIndexed(predicate: (Int, Char) -> Boolean): String { + return filterIndexedTo(StringBuilder(), predicate).toString() +} + +/** + * Appends all characters matching the given [predicate] to the given [destination]. + */ +public inline fun CharSequence.filterIndexedTo(destination: C, predicate: (Int, Char) -> Boolean): C { + forEachIndexed { index, element -> + if (predicate(index, element)) destination.append(element) + } + return destination +} + /** * Returns a char sequence containing only those characters from the original char sequence that do not match the given [predicate]. */ @@ -560,7 +584,7 @@ public inline fun CharSequence.filterTo(destination: C, predica } /** - * Appends all elements matching the given [predicate] into the given [destination]. + * Appends all characters matching the given [predicate] to the given [destination]. */ @Deprecated("Provided for binary compatibility", level = DeprecationLevel.HIDDEN) public inline fun String.filterTo(destination: C, predicate: (Char) -> Boolean): C { diff --git a/libraries/stdlib/test/collections/ArraysTest.kt b/libraries/stdlib/test/collections/ArraysTest.kt index 55ea5f05790..d862b0bac48 100644 --- a/libraries/stdlib/test/collections/ArraysTest.kt +++ b/libraries/stdlib/test/collections/ArraysTest.kt @@ -736,6 +736,15 @@ class ArraysTest { expect(listOf("b"), { arrayOf("a", "b").filter { it > "a" } }) } + @test fun filterIndexed() { + expect(listOf(), { intArrayOf().filterIndexed { i, v -> i > v } }) + expect(listOf(2, 5, 8), { intArrayOf(2, 4, 3, 5, 8).filterIndexed { index, value -> index % 2 == value % 2 } }) + expect(listOf(2, 5, 8), { longArrayOf(2, 4, 3, 5, 8).filterIndexed { index, value -> index % 2 == (value % 2).toInt() } }) + expect(listOf(2, 5, 8), { byteArrayOf(2, 4, 3, 5, 8).filterIndexed { index, value -> index % 2 == (value % 2).toInt() } }) + expect(listOf('9', 'e', 'a'), { charArrayOf('9', 'e', 'd', 'a').filterIndexed { index, c -> c == 'a' || index < 2 }}) + expect(listOf("a", "c", "d"), { arrayOf("a", "b", "c", "d").filterIndexed { index, s -> s == "a" || index >= 2 } }) + } + @test fun filterNot() { expect(listOf(), { intArrayOf().filterNot { it > 2 } }) expect(listOf(1), { intArrayOf(1).filterNot { it > 2 } }) diff --git a/libraries/stdlib/test/collections/IterableTests.kt b/libraries/stdlib/test/collections/IterableTests.kt index d0ea310aa44..5ba52e14044 100644 --- a/libraries/stdlib/test/collections/IterableTests.kt +++ b/libraries/stdlib/test/collections/IterableTests.kt @@ -146,6 +146,11 @@ abstract class IterableTests>(val data: T, val empty: T) { assertEquals(listOf("foo"), foo) } + @Test fun filterIndexed() { + val result = data.filterIndexed { index, value -> value.first() == ('a' + index) } + assertEquals(listOf("bar"), result) + } + @Test fun drop() { val foo = data.drop(1) diff --git a/libraries/stdlib/test/collections/SequenceTest.kt b/libraries/stdlib/test/collections/SequenceTest.kt index b54a297bd1f..8d98526e9ea 100644 --- a/libraries/stdlib/test/collections/SequenceTest.kt +++ b/libraries/stdlib/test/collections/SequenceTest.kt @@ -45,6 +45,10 @@ public class SequenceTest { } } + @test fun filterIndexed() { + assertEquals(listOf(1, 2, 5, 13, 34), fibonacci().filterIndexed { index, value -> index % 2 == 1 }.take(5).toList()) + } + @test fun filterNullable() { val data = sequenceOf(null, "foo", null, "bar") val filtered = data.filter { it == null || it == "foo" } diff --git a/libraries/stdlib/test/text/StringTest.kt b/libraries/stdlib/test/text/StringTest.kt index ea72873484e..100c43e91dd 100644 --- a/libraries/stdlib/test/text/StringTest.kt +++ b/libraries/stdlib/test/text/StringTest.kt @@ -746,6 +746,15 @@ class StringTest { assertContentEquals("abcd", arg1("a1b2c3d4").filterNot { it.isAsciiDigit() }) } + @test fun filterIndexed() { + val data = "abedcf" + assertEquals("abdf", data.filterIndexed { index, c -> c == 'a' + index }) + } + + @test fun filterIndexedCharSequence() = withOneCharSequenceArg("abedcf") { data -> + assertContentEquals("abdf", data.filterIndexed { index, c -> c == 'a' + index }) + } + @test fun all() = withOneCharSequenceArg("AbCd") { data -> assertTrue { data.all { it.isAsciiLetter() } diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Filtering.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Filtering.kt index 3bcb88471bd..857f471e605 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Filtering.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Filtering.kt @@ -3,6 +3,7 @@ package templates import templates.Family.* import templates.DocExtensions.collection import templates.DocExtensions.element +import templates.DocExtensions.mapResult fun filtering(): List { val templates = arrayListOf() @@ -351,7 +352,7 @@ fun filtering(): List { templates add f("filter(predicate: (T) -> Boolean)") { inline(true) - doc { "Returns a list containing all elements matching the given [predicate]." } + doc { f -> "Returns a ${f.mapResult} containing only ${f.element}s matching the given [predicate]." } returns("List") body { """ @@ -364,7 +365,6 @@ fun filtering(): List { body(CharSequences, Strings) { f -> """return filterTo(StringBuilder(), predicate)${toResult(f)}""" } inline(false, Sequences) - doc(Sequences) { "Returns a sequence containing all elements matching the given [predicate]." } returns(Sequences) { "Sequence" } body(Sequences) { """ @@ -376,7 +376,7 @@ fun filtering(): List { templates add f("filterTo(destination: C, predicate: (T) -> Boolean)") { inline(true) - doc { "Appends all elements matching the given [predicate] into the given [destination]." } + doc { f -> "Appends all ${f.element}s matching the given [predicate] to the given [destination]." } typeParam("C : TCollection") returns("C") @@ -388,7 +388,6 @@ fun filtering(): List { } deprecate(Strings) { forBinaryCompatibility } - doc(CharSequences) { "Appends all characters matching the given [predicate] to the given [destination]." } body(CharSequences, Strings) { """ for (index in 0..length() - 1) { @@ -400,6 +399,51 @@ fun filtering(): List { } } + templates add f("filterIndexed(predicate: (Int, T) -> Boolean)") { + inline(true) + + doc { f -> "Returns a ${f.mapResult} containing only ${f.element}s matching the given [predicate]." } + returns("List") + body { + """ + return filterIndexedTo(ArrayList(), predicate) + """ + } + + doc(CharSequences, Strings) { f -> "Returns a ${f.collection} containing only those characters from the original ${f.collection} that match the given [predicate]." } + returns(CharSequences, Strings) { "SELF" } + body(CharSequences, Strings) { f -> """return filterIndexedTo(StringBuilder(), predicate)${toResult(f)}""" } + + inline(false, Sequences) + returns(Sequences) { "Sequence" } + body(Sequences) { + """ + // TODO: Rewrite with generalized MapFilterIndexingSequence + return TransformingSequence(FilteringSequence(IndexingSequence(this), true, { predicate(it.index, it.value) }), { it.value }) + """ + } + } + + + templates add f("filterIndexedTo(destination: C, predicate: (Int, T) -> Boolean)") { + inline(true) + + include(CharSequences) + + doc { f -> "Appends all ${f.element}s matching the given [predicate] to the given [destination]." } + typeParam("C : TCollection") + returns("C") + + body { f -> + """ + forEachIndexed { index, element -> + if (predicate(index, element)) destination.${ if (f==CharSequences) "append" else "add" }(element) + } + return destination + """ + } + } + templates add f("filterNot(predicate: (T) -> Boolean)") { inline(true)