Provide stable sorting when JS engine sorting doesn't look stable

#KT-12473
This commit is contained in:
Ilya Gorbunov
2019-01-10 19:29:53 +03:00
parent 7c3c454654
commit 51eb21d44b
5 changed files with 176 additions and 31 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
@@ -1412,6 +1412,21 @@ class ArraysTest {
}
}
@Test fun sortStable() {
val keyRange = 'A'..'D'
for (size in listOf(10, 100, 2000)) {
val array = Array(size) { index -> Sortable(keyRange.random(), index) }
array.sortedArray().assertStableSorted()
array.sortedArrayDescending().assertStableSorted(descending = true)
array.sort()
array.assertStableSorted()
array.sortDescending()
array.assertStableSorted(descending = true)
}
}
@Test fun sortByInPlace() {
val data = arrayOf("aa" to 20, "ab" to 3, "aa" to 3)
data.sortBy { it.second }
@@ -1431,6 +1446,22 @@ class ArraysTest {
assertEquals(listOf(1, 2, 0), indices.sortedBy { values[it] })
}
@Test fun sortByStable() {
val keyRange = 'A'..'D'
for (size in listOf(10, 100, 2000)) {
val array = Array(size) { index -> Sortable(keyRange.random(), index) }
array.sortedBy { it.key }.iterator().assertStableSorted()
array.sortedByDescending { it.key }.iterator().assertStableSorted(descending = true)
array.sortBy { it.key }
array.assertStableSorted()
array.sortByDescending { it.key }
array.assertStableSorted(descending = true)
}
}
@Test fun sortedNullableBy() {
fun String.nullIfEmpty() = if (this.isEmpty()) null else this
arrayOf(null, "").let {
@@ -1457,6 +1488,20 @@ class ArraysTest {
}
}
private data class Sortable<K : Comparable<K>>(val key: K, val index: Int) : Comparable<Sortable<K>> {
override fun compareTo(other: Sortable<K>): Int = this.key.compareTo(other.key)
}
private fun <K : Comparable<K>> Array<out Sortable<K>>.assertStableSorted(descending: Boolean = false) =
iterator().assertStableSorted(descending = descending)
private fun <K : Comparable<K>> Iterator<Sortable<K>>.assertStableSorted(descending: Boolean = false) {
assertSorted { a, b ->
val relation = a.key.compareTo(b.key)
(if (descending) relation > 0 else relation < 0) || relation == 0 && a.index < b.index
}
}
private class ArraySortedChecker<A, T>(val array: A, val comparator: Comparator<in T>) {
public fun <R> checkSorted(sorted: A.() -> R, sortedDescending: A.() -> R, iterator: R.() -> Iterator<T>) {
array.sorted().iterator().assertSorted { a, b -> comparator.compare(a, b) <= 0 }