From c8094e7587cab91254902cbdc00404cfa84faf0a Mon Sep 17 00:00:00 2001 From: Valery Kharitonov Date: Sun, 4 Jan 2015 14:12:32 +0300 Subject: [PATCH] Implemented indexOfFirst/Last(predicate). #KT-6577 Fixed --- libraries/stdlib/src/generated/_Elements.kt | 338 ++++++++++++++++-- .../stdlib/test/collections/ArraysTest.kt | 32 +- .../stdlib/test/collections/IterableTests.kt | 15 +- .../src/templates/Elements.kt | 67 +++- 4 files changed, 426 insertions(+), 26 deletions(-) diff --git a/libraries/stdlib/src/generated/_Elements.kt b/libraries/stdlib/src/generated/_Elements.kt index 80c0d3dc818..2c5edcbeed2 100644 --- a/libraries/stdlib/src/generated/_Elements.kt +++ b/libraries/stdlib/src/generated/_Elements.kt @@ -1162,6 +1162,324 @@ public fun Stream.indexOf(element: T): Int { return -1 } +/** + * Returns index of the first element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun Array.indexOfFirst(predicate: (T) -> Boolean): Int { + for (index in indices) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the first element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun BooleanArray.indexOfFirst(predicate: (Boolean) -> Boolean): Int { + for (index in indices) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the first element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun ByteArray.indexOfFirst(predicate: (Byte) -> Boolean): Int { + for (index in indices) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the first element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun CharArray.indexOfFirst(predicate: (Char) -> Boolean): Int { + for (index in indices) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the first element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun DoubleArray.indexOfFirst(predicate: (Double) -> Boolean): Int { + for (index in indices) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the first element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun FloatArray.indexOfFirst(predicate: (Float) -> Boolean): Int { + for (index in indices) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the first element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun IntArray.indexOfFirst(predicate: (Int) -> Boolean): Int { + for (index in indices) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the first element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun LongArray.indexOfFirst(predicate: (Long) -> Boolean): Int { + for (index in indices) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the first element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun ShortArray.indexOfFirst(predicate: (Short) -> Boolean): Int { + for (index in indices) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the first element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun Iterable.indexOfFirst(predicate: (T) -> Boolean): Int { + var index = 0 + for (item in this) { + if (predicate(item)) + return index + index++ + } + return -1 +} + +/** + * Returns index of the first element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun List.indexOfFirst(predicate: (T) -> Boolean): Int { + for (index in indices) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the first element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun Stream.indexOfFirst(predicate: (T) -> Boolean): Int { + var index = 0 + for (item in this) { + if (predicate(item)) + return index + index++ + } + return -1 +} + +/** + * Returns index of the first element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun String.indexOfFirst(predicate: (Char) -> Boolean): Int { + for (index in indices) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the last element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun Array.indexOfLast(predicate: (T) -> Boolean): Int { + for (index in indices.reversed()) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the last element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun BooleanArray.indexOfLast(predicate: (Boolean) -> Boolean): Int { + for (index in indices.reversed()) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the last element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun ByteArray.indexOfLast(predicate: (Byte) -> Boolean): Int { + for (index in indices.reversed()) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the last element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun CharArray.indexOfLast(predicate: (Char) -> Boolean): Int { + for (index in indices.reversed()) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the last element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun DoubleArray.indexOfLast(predicate: (Double) -> Boolean): Int { + for (index in indices.reversed()) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the last element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun FloatArray.indexOfLast(predicate: (Float) -> Boolean): Int { + for (index in indices.reversed()) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the last element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun IntArray.indexOfLast(predicate: (Int) -> Boolean): Int { + for (index in indices.reversed()) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the last element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun LongArray.indexOfLast(predicate: (Long) -> Boolean): Int { + for (index in indices.reversed()) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the last element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun ShortArray.indexOfLast(predicate: (Short) -> Boolean): Int { + for (index in indices.reversed()) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the last element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun Iterable.indexOfLast(predicate: (T) -> Boolean): Int { + var lastIndex = -1 + var index = 0 + for (item in this) { + if (predicate(item)) + lastIndex = index + index++ + } + return lastIndex +} + +/** + * Returns index of the last element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun List.indexOfLast(predicate: (T) -> Boolean): Int { + for (index in indices.reversed()) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + +/** + * Returns index of the last element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun Stream.indexOfLast(predicate: (T) -> Boolean): Int { + var lastIndex = -1 + var index = 0 + for (item in this) { + if (predicate(item)) + lastIndex = index + index++ + } + return lastIndex +} + +/** + * Returns index of the last element matching the given [predicate], or -1 if the collection does not contain such element + */ +public inline fun String.indexOfLast(predicate: (Char) -> Boolean): Int { + for (index in indices.reversed()) { + if (predicate(this[index])) { + return index + } + } + return -1 +} + /** * Returns the last element. * @throws NoSuchElementException if the collection is empty. @@ -1644,26 +1962,6 @@ public fun Iterable.lastIndexOf(element: T): Int { return lastIndex } -/** - * Returns last index of *element*, or -1 if the collection does not contain element - */ -public fun List.lastIndexOf(element: T): Int { - if (element == null) { - for (index in indices.reverse()) { - if (this[index] == null) { - return index - } - } - } else { - for (index in indices.reverse()) { - if (element == this[index]) { - return index - } - } - } - return -1 -} - /** * Returns last index of *element*, or -1 if the collection does not contain element */ diff --git a/libraries/stdlib/test/collections/ArraysTest.kt b/libraries/stdlib/test/collections/ArraysTest.kt index d7957ccb503..bf4bd15ae2b 100644 --- a/libraries/stdlib/test/collections/ArraysTest.kt +++ b/libraries/stdlib/test/collections/ArraysTest.kt @@ -165,7 +165,37 @@ class ArraysTest { expect(0) { array("cat", "dog", "bird").indexOf("cat") } expect(1) { array("cat", "dog", "bird").indexOf("dog") } expect(2) { array("cat", "dog", "bird").indexOf("bird") } - expect(0) { array(null, "dog", null).indexOf(null) } + expect(0) { array(null, "dog", null).indexOf(null : String?)} + + expect(-1) { array("cat", "dog", "bird").indexOfFirst { it.contains("p") } } + expect(0) { array("cat", "dog", "bird").indexOfFirst { it.startsWith('c') } } + expect(1) { array("cat", "dog", "bird").indexOfFirst { it.startsWith('d') } } + expect(2) { array("cat", "dog", "bird").indexOfFirst { it.endsWith('d') } } + + expect(-1) { streamOf("cat", "dog", "bird").indexOfFirst { it.contains("p") } } + expect(0) { streamOf("cat", "dog", "bird").indexOfFirst { it.startsWith('c') } } + expect(1) { streamOf("cat", "dog", "bird").indexOfFirst { it.startsWith('d') } } + expect(2) { streamOf("cat", "dog", "bird").indexOfFirst { it.endsWith('d') } } + } + + test fun lastIndexOf() { + expect(-1) { array("cat", "dog", "bird").lastIndexOf("mouse") } + expect(0) { array("cat", "dog", "bird").lastIndexOf("cat") } + expect(1) { array("cat", "dog", "bird").lastIndexOf("dog") } + expect(2) { array(null, "dog", null).lastIndexOf(null : String?)} + expect(3) { array("cat", "dog", "bird", "dog").lastIndexOf("dog") } + + expect(-1) { array("cat", "dog", "bird").indexOfLast { it.contains("p") } } + expect(0) { array("cat", "dog", "bird").indexOfLast { it.startsWith('c') } } + expect(2) { array("cat", "dog", "cap", "bird").indexOfLast { it.startsWith('c') } } + expect(2) { array("cat", "dog", "bird").indexOfLast { it.endsWith('d') } } + expect(3) { array("cat", "dog", "bird", "red").indexOfLast { it.endsWith('d') } } + + expect(-1) { streamOf("cat", "dog", "bird").indexOfLast { it.contains("p") } } + expect(0) { streamOf("cat", "dog", "bird").indexOfLast { it.startsWith('c') } } + expect(2) { streamOf("cat", "dog", "cap", "bird").indexOfLast { it.startsWith('c') } } + expect(2) { streamOf("cat", "dog", "bird").indexOfLast { it.endsWith('d') } } + expect(3) { streamOf("cat", "dog", "bird", "red").indexOfLast { it.endsWith('d') } } } test fun plus() { diff --git a/libraries/stdlib/test/collections/IterableTests.kt b/libraries/stdlib/test/collections/IterableTests.kt index 0b58c462047..3077a511253 100644 --- a/libraries/stdlib/test/collections/IterableTests.kt +++ b/libraries/stdlib/test/collections/IterableTests.kt @@ -1,8 +1,9 @@ package test.collections import org.junit.Test +import java.util.ArrayList +import java.util.LinkedHashSet import kotlin.test.* -import java.util.* fun iterableOf(vararg items: T): Iterable = IterableWrapper(items.toList()) @@ -35,6 +36,18 @@ abstract class OrderedIterableTests>(data: T, empty: T) : I expect(-1) { data.lastIndexOf("zap") } } + Test fun indexOfFirst() { + expect(-1) { data.indexOfFirst { it.contains("p") } } + expect(0) { data.indexOfFirst { it.startsWith('f') } } + expect(-1) { empty.indexOfFirst { it.startsWith('f') } } + } + + Test fun indexOfLast() { + expect(-1) { data.indexOfLast { it.contains("p") } } + expect(1) { data.indexOfLast { it.length() == 3 } } + expect(-1) { empty.indexOfLast { it.startsWith('f') } } + } + Test fun elementAt() { expect("foo") { data.elementAt(0) } expect("bar") { data.elementAt(1) } diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt index 259e1b2b5a7..21b8954d99a 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt @@ -24,7 +24,7 @@ fun elements(): List { } templates add f("indexOf(element: T)") { - exclude(Strings) + exclude(Strings, Lists) // has native implementation doc { "Returns first index of [element], or -1 if the collection does not contain element" } returns("Int") body { @@ -70,7 +70,7 @@ fun elements(): List { } templates add f("lastIndexOf(element: T)") { - exclude(Strings) // has native implementation + exclude(Strings, Lists) // has native implementation doc { "Returns last index of *element*, or -1 if the collection does not contain element" } returns("Int") body { @@ -86,7 +86,7 @@ fun elements(): List { """ } - body(Lists, ArraysOfObjects) { + body(ArraysOfObjects) { """ if (element == null) { for (index in indices.reverse()) { @@ -116,6 +116,65 @@ fun elements(): List { } } + templates add f("indexOfFirst(predicate: (T) -> Boolean)") { + inline(true) + + doc { "Returns index of the first element matching the given [predicate], or -1 if the collection does not contain such element" } + returns("Int") + body { + """ + var index = 0 + for (item in this) { + if (predicate(item)) + return index + index++ + } + return -1 + """ + } + + body(Lists, Strings, ArraysOfPrimitives, ArraysOfObjects) { + """ + for (index in indices) { + if (predicate(this[index])) { + return index + } + } + return -1 + """ + } + } + + templates add f("indexOfLast(predicate: (T) -> Boolean)") { + inline(true) + + doc { "Returns index of the last element matching the given [predicate], or -1 if the collection does not contain such element" } + returns("Int") + body { + """ + var lastIndex = -1 + var index = 0 + for (item in this) { + if (predicate(item)) + lastIndex = index + index++ + } + return lastIndex + """ + } + + body(Lists, Strings, ArraysOfPrimitives, ArraysOfObjects) { + """ + for (index in indices.reversed()) { + if (predicate(this[index])) { + return index + } + } + return -1 + """ + } + } + templates add f("elementAt(index: Int)") { doc { "Returns element at given *index*" } returns("T") @@ -533,4 +592,4 @@ fun elements(): List { } return templates -} \ No newline at end of file +}