Another approach for sorting nulls.

This commit is contained in:
Ilya Gorbunov
2015-08-05 21:43:02 +03:00
parent d49a1973e5
commit 5a4e598ba7
4 changed files with 40 additions and 22 deletions
+23 -11
View File
@@ -68,13 +68,19 @@ public inline fun <T> compareValuesBy(a: T, b: T, selector: (T) -> Comparable<*>
return compareValues(selector(a), selector(b)) return compareValues(selector(a), selector(b))
} }
public inline fun <T, K> compareValuesBy(a: T, b: T, comparator: Comparator<K>, selector: (T) -> K): Int {
return comparator.compare(selector(a), selector(b))
}
// Not so useful without type inference for receiver of expression // Not so useful without type inference for receiver of expression
// compareValuesWith(v1, v2, compareBy { it.prop1 } thenByDescending { it.prop2 }) // compareValuesWith(v1, v2, compareBy { it.prop1 } thenByDescending { it.prop2 })
///** /**
// * Compares two values using the specified [comparator]. * Compares two values using the specified [comparator].
// */ */
//@suppress("NOTHING_TO_INLINE") @suppress("NOTHING_TO_INLINE")
//public inline fun <T> compareValuesWith(a: T, b: T, comparator: Comparator<T>): Int = comparator.compare(a, b) public inline fun <T> compareValuesWith(a: T, b: T, comparator: Comparator<T>): Int = comparator.compare(a, b)
/** /**
* Compares two nullable [Comparable] values. Null is considered less than any value. * Compares two nullable [Comparable] values. Null is considered less than any value.
@@ -114,6 +120,12 @@ inline public fun <T> compareBy(inlineOptions(InlineOption.ONLY_LOCAL_RETURN) co
} }
} }
public fun <T, K> compareBy(comparator: Comparator<K>, selector: (T) -> K): Comparator<T> {
return object : Comparator<T> {
public override fun compare(a: T, b: T): Int = compareValuesBy(a, b, comparator, selector)
}
}
/** /**
* Creates a descending comparator using the function to transform value to a [Comparable] instance for comparison. * Creates a descending comparator using the function to transform value to a [Comparable] instance for comparison.
*/ */
@@ -172,31 +184,31 @@ inline public fun <T> Comparator<T>.thenComparator(inlineOptions(InlineOption.ON
// Not so useful without type inference for receiver of expression // Not so useful without type inference for receiver of expression
/** /**
* Extends the given comparator of non-nullable values to a comparator of nullable values * Extends the given [comparator] of non-nullable values to a comparator of nullable values
* considering null value less than any other value. * considering null value less than any other value.
*/ */
public fun <T: Any> Comparator<T>.nullsFirst(): Comparator<T?> { public fun <T: Any> nullsFirst(comparator: Comparator<T>): Comparator<T?> {
return object: Comparator<T?> { return object: Comparator<T?> {
override fun compare(a: T?, b: T?): Int { override fun compare(a: T?, b: T?): Int {
if (a === b) return 0 if (a === b) return 0
if (a == null) return -1 if (a == null) return -1
if (b == null) return 1 if (b == null) return 1
return this@nullsFirst.compare(a, b) return comparator.compare(a, b)
} }
} }
} }
/** /**
* Extends the given comparator of non-nullable values to a comparator of nullable values * Extends the given [comparator] of non-nullable values to a comparator of nullable values
* considering null value greater than any other value. * considering null value greater than any other value.
*/ */
public fun <T: Any> Comparator<T>.nullsLast(): Comparator<T?> { public fun <T: Any> nullsLast(comparator: Comparator<T>): Comparator<T?> {
return object: Comparator<T?> { return object: Comparator<T?> {
override fun compare(a: T?, b: T?): Int { override fun compare(a: T?, b: T?): Int {
if (a === b) return 0 if (a === b) return 0
if (a == null) return 1 if (a == null) return 1
if (b == null) return -1 if (b == null) return -1
return this@nullsLast.compare(a, b) return comparator.compare(a, b)
} }
} }
} }
+1 -1
View File
@@ -39,7 +39,7 @@ class OrderingTest {
val v2: Item? = null val v2: Item? = null
val diff = compareValuesBy(v1, v2) { it?.rating } val diff = compareValuesBy(v1, v2) { it?.rating }
assertTrue(diff > 0) assertTrue(diff > 0)
val diff2 = compareBy<Item> { it.rating }.thenBy { it.name }.nullsLast().compare(v1, v2) val diff2 = nullsLast(compareBy<Item> { it.rating }.thenBy { it.name }).compare(v1, v2)
assertTrue(diff2 < 0) assertTrue(diff2 < 0)
} }
@@ -827,9 +827,9 @@ class ArraysTest {
test fun sortedNullableBy() { test fun sortedNullableBy() {
fun String.nullIfEmpty() = if (isEmpty()) null else this fun String.nullIfEmpty() = if (isEmpty()) null else this
arrayOf(null, "").let { arrayOf(null, "").let {
expect(listOf(null, "")) { it.sortedWith(compareBy<String> { it }.nullsFirst()) } expect(listOf(null, "")) { it.sortedWith(nullsFirst(compareBy { it })) }
expect(listOf("", null)) { it.sortedWith(compareByDescending<String> { it }.nullsLast()) } expect(listOf("", null)) { it.sortedWith(nullsLast(compareByDescending { it })) }
expect(listOf("", null)) { it.sortedWith(compareByDescending<String> { it.nullIfEmpty() }.nullsLast() ) } expect(listOf("", null)) { it.sortedWith(nullsLast(compareByDescending { it.nullIfEmpty() })) }
} }
} }
@@ -572,13 +572,19 @@ class CollectionTest {
test fun sortedNullableBy() { test fun sortedNullableBy() {
fun String.nullIfEmpty() = if (isEmpty()) null else this fun String.nullIfEmpty() = if (isEmpty()) null else this
listOf(null, "").let { listOf(null, "", "a").let {
// expect(listOf(null, "")) { it.sortedBy { it.orEmpty()}} expect(listOf(null, "", "a")) { it.sortedWith(nullsFirst(compareBy { it })) }
// expect(listOf("", null)) { it.sortedByDescending { it.orEmpty() }} expect(listOf("a", "", null)) { it.sortedWith(nullsLast(compareByDescending { it })) }
// expect(listOf("", null)) { it.sortedByDescending { it?.nullIfEmpty() }} expect(listOf(null, "a", "")) { it.sortedWith(nullsFirst(compareByDescending { it.nullIfEmpty() })) }
expect(listOf(null, "")) { it.sortedWith(compareBy<String> { it }.nullsFirst()) } }
expect(listOf("", null)) { it.sortedWith(compareByDescending<String> { it }.nullsLast()) } }
expect(listOf("", null)) { it.sortedWith(compareByDescending<String> { it.nullIfEmpty() }.nullsLast() ) }
test fun sortedByNullable() {
fun String.nonEmptyLength() = if (isEmpty()) null else length()
listOf("", "sort", "abc").let {
assertEquals(listOf("", "abc", "sort"), it.sortedBy { it.nonEmptyLength() })
assertEquals(listOf("sort", "abc", ""), it.sortedByDescending { it.nonEmptyLength() })
assertEquals(listOf("abc", "sort", ""), it.sortedWith(compareBy(nullsLast(compareBy<Int> {it})) { it.nonEmptyLength()}))
} }
} }