Skip to content

Commit

Permalink
Merge pull request #581 from hexagonkt/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
jaguililla committed Dec 9, 2022
2 parents d206f4d + 06ec37c commit ecc60f1
Show file tree
Hide file tree
Showing 16 changed files with 169 additions and 2,907 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ plugins {
id("com.github.jk1.dependency-license-report") version("2.1")
id("io.gitlab.arturbosch.detekt") version("1.22.0") apply(false)
id("me.champeau.jmh") version("0.6.8") apply(false)
id("org.graalvm.buildtools.native") version("0.9.18") apply(false)
id("org.graalvm.buildtools.native") version("0.9.19") apply(false)
}

apply(from = "gradle/certificates.gradle")
Expand Down
102 changes: 66 additions & 36 deletions core/src/main/kotlin/com/hexagonkt/core/Data.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@ package com.hexagonkt.core

import kotlin.reflect.KProperty1

/**
* [TODO](https://github.com/hexagonkt/hexagon/issues/271).
*
* @param mapA .
* @param mapB .
* @return .
*/
fun merge(mapA: Map<*, *>, mapB: Map<*, *>): Map<*, *> =
(mapA.entries + mapB.entries)
.groupBy { it.key }
.mapValues { (_, v) -> v.map { it.value } }
.mapValues { (_, v) ->
val isList = v.all { it is Collection<*> }
val isMap = v.all { it is Map<*, *> }
when {
isList -> v.map { it as Collection<*> }.reduce { a, b -> a + b }
isMap -> v.map { it as Map<*, *> }.reduce { a, b -> merge(a, b) }
else -> v.last()
}
}

/**
* [TODO](https://github.com/hexagonkt/hexagon/issues/271).
*
Expand Down Expand Up @@ -89,6 +110,15 @@ fun <T : Any> fieldsMapOf(vararg fields: Pair<KProperty1<T, *>, *>): Map<String,
fun <T : Any> fieldsMapOfNotNull(vararg fields: Pair<KProperty1<T, *>, *>): Map<String, *> =
fieldsMapOf(*fields).filterValues { it != null }

/**
* [TODO](https://github.com/hexagonkt/hexagon/issues/271).
*
* @param pairs .
* @return .
*/
fun <K : Any> mapOfNotNull(vararg pairs: Pair<K, *>): Map<K, *> =
mapOf(*pairs).filterValues { it != null }

/**
* [TODO](https://github.com/hexagonkt/hexagon/issues/271).
*
Expand All @@ -105,7 +135,7 @@ fun <K, V> Map<K, V>.require(name: K): V =
* @receiver .
* @return .
*/
fun <K, V> Map<K, V?>.filterEmpty(): Map<K, V> =
fun <K, V> Map<K, V?>.filterNotEmpty(): Map<K, V> =
this.filterValues(::notEmpty).mapValues { (_, v) -> v ?: fail }

/**
Expand All @@ -114,7 +144,7 @@ fun <K, V> Map<K, V?>.filterEmpty(): Map<K, V> =
* @receiver .
* @return .
*/
fun <V> List<V?>.filterEmpty(): List<V> =
fun <V> List<V?>.filterNotEmpty(): List<V> =
this.filter(::notEmpty).map { it ?: fail }

/**
Expand All @@ -123,31 +153,31 @@ fun <V> List<V?>.filterEmpty(): List<V> =
* @receiver .
* @return .
*/
fun Map<*, *>.filterEmptyRecursive(): Map<*, *> =
fun Map<*, *>.filterNotEmptyRecursive(): Map<*, *> =
mapValues { (_, v) ->
when (v) {
is List<*> -> v.filterEmptyRecursive()
is Map<*, *> -> v.filterEmptyRecursive()
is List<*> -> v.filterNotEmptyRecursive()
is Map<*, *> -> v.filterNotEmptyRecursive()
else -> v
}
}
.filterEmpty()
.filterNotEmpty()

/**
* [TODO](https://github.com/hexagonkt/hexagon/issues/271).
*
* @receiver .
* @return .
*/
fun List<*>.filterEmptyRecursive(): List<*> =
fun List<*>.filterNotEmptyRecursive(): List<*> =
map {
when (it) {
is List<*> -> it.filterEmptyRecursive()
is Map<*, *> -> it.filterEmptyRecursive()
is List<*> -> it.filterNotEmptyRecursive()
is Map<*, *> -> it.filterNotEmptyRecursive()
else -> it
}
}
.filterEmpty()
.filterNotEmpty()

/**
* [TODO](https://github.com/hexagonkt/hexagon/issues/271).
Expand Down Expand Up @@ -185,64 +215,64 @@ fun Map<*, *>.getBoolean(key: KProperty1<*, *>): Boolean? =
fun Map<*, *>.getString(key: KProperty1<*, *>): String? =
getKey(key)

fun Map<*, *>.getList(key: KProperty1<*, *>): List<*>? =
fun Map<*, *>.getList(key: KProperty1<*, *>): Collection<*>? =
getKey(key)

fun Map<*, *>.getMap(key: KProperty1<*, *>): Map<String, *>? =
getKey(key)

fun Map<*, *>.getInts(key: KProperty1<*, *>): List<Int>? =
fun Map<*, *>.getInts(key: KProperty1<*, *>): Collection<Int>? =
getKey(key)

fun Map<*, *>.getLongs(key: KProperty1<*, *>): List<Long>? =
fun Map<*, *>.getLongs(key: KProperty1<*, *>): Collection<Long>? =
getKey(key)

fun Map<*, *>.getFloats(key: KProperty1<*, *>): List<Float>? =
fun Map<*, *>.getFloats(key: KProperty1<*, *>): Collection<Float>? =
getKey(key)

fun Map<*, *>.getDoubles(key: KProperty1<*, *>): List<Double>? =
fun Map<*, *>.getDoubles(key: KProperty1<*, *>): Collection<Double>? =
getKey(key)

fun Map<*, *>.getBooleans(key: KProperty1<*, *>): List<Boolean>? =
fun Map<*, *>.getBooleans(key: KProperty1<*, *>): Collection<Boolean>? =
getKey(key)

fun Map<*, *>.getStrings(key: KProperty1<*, *>): List<String>? =
fun Map<*, *>.getStrings(key: KProperty1<*, *>): Collection<String>? =
getKey(key)

fun Map<*, *>.getLists(key: KProperty1<*, *>): List<List<*>>? =
fun Map<*, *>.getLists(key: KProperty1<*, *>): Collection<List<*>>? =
getKey(key)

fun Map<*, *>.getMaps(key: KProperty1<*, *>): List<Map<String, *>>? =
fun Map<*, *>.getMaps(key: KProperty1<*, *>): Collection<Map<String, *>>? =
getKey(key)

fun Map<*, *>.getListOrEmpty(key: KProperty1<*, *>): List<*> =
fun Map<*, *>.getListOrEmpty(key: KProperty1<*, *>): Collection<*> =
getList(key) ?: emptyList<Any>()

fun Map<*, *>.getMapOrEmpty(key: KProperty1<*, *>): Map<String, *> =
getMap(key) ?: emptyMap<String, Any>()

fun Map<*, *>.getIntsOrEmpty(key: KProperty1<*, *>): List<Int> =
fun Map<*, *>.getIntsOrEmpty(key: KProperty1<*, *>): Collection<Int> =
getInts(key) ?: emptyList()

fun Map<*, *>.getLongsOrEmpty(key: KProperty1<*, *>): List<Long> =
fun Map<*, *>.getLongsOrEmpty(key: KProperty1<*, *>): Collection<Long> =
getLongs(key) ?: emptyList()

fun Map<*, *>.getFloatsOrEmpty(key: KProperty1<*, *>): List<Float> =
fun Map<*, *>.getFloatsOrEmpty(key: KProperty1<*, *>): Collection<Float> =
getFloats(key) ?: emptyList()

fun Map<*, *>.getDoublesOrEmpty(key: KProperty1<*, *>): List<Double> =
fun Map<*, *>.getDoublesOrEmpty(key: KProperty1<*, *>): Collection<Double> =
getDoubles(key) ?: emptyList()

fun Map<*, *>.getBooleansOrEmpty(key: KProperty1<*, *>): List<Boolean> =
fun Map<*, *>.getBooleansOrEmpty(key: KProperty1<*, *>): Collection<Boolean> =
getBooleans(key) ?: emptyList()

fun Map<*, *>.getStringsOrEmpty(key: KProperty1<*, *>): List<String> =
fun Map<*, *>.getStringsOrEmpty(key: KProperty1<*, *>): Collection<String> =
getStrings(key) ?: emptyList()

fun Map<*, *>.getListsOrEmpty(key: KProperty1<*, *>): List<List<*>> =
fun Map<*, *>.getListsOrEmpty(key: KProperty1<*, *>): Collection<Collection<*>> =
getLists(key) ?: emptyList()

fun Map<*, *>.getMapsOrEmpty(key: KProperty1<*, *>): List<Map<String, *>> =
fun Map<*, *>.getMapsOrEmpty(key: KProperty1<*, *>): Collection<Map<String, *>> =
getMaps(key) ?: emptyList()

inline fun <reified T : Any> Map<*, *>.requireKey(key: KProperty1<*, *>): T =
Expand All @@ -267,7 +297,7 @@ fun Map<*, *>.requireBoolean(key: KProperty1<*, *>): Boolean =
fun Map<*, *>.requireString(key: KProperty1<*, *>): String =
requireKey(key)

fun Map<*, *>.requireList(key: KProperty1<*, *>): List<*> =
fun Map<*, *>.requireList(key: KProperty1<*, *>): Collection<*> =
requireKey(key)

fun Map<*, *>.requireMap(key: KProperty1<*, *>): Map<String, *> =
Expand All @@ -276,23 +306,23 @@ fun Map<*, *>.requireMap(key: KProperty1<*, *>): Map<String, *> =
fun Map<*, *>.requireInts(key: KProperty1<*, *>): List<Int> =
requireKey(key)

fun Map<*, *>.requireLongs(key: KProperty1<*, *>): List<Long> =
fun Map<*, *>.requireLongs(key: KProperty1<*, *>): Collection<Long> =
requireKey(key)

fun Map<*, *>.requireFloats(key: KProperty1<*, *>): List<Float> =
fun Map<*, *>.requireFloats(key: KProperty1<*, *>): Collection<Float> =
requireKey(key)

fun Map<*, *>.requireDoubles(key: KProperty1<*, *>): List<Double> =
fun Map<*, *>.requireDoubles(key: KProperty1<*, *>): Collection<Double> =
requireKey(key)

fun Map<*, *>.requireBooleans(key: KProperty1<*, *>): List<Boolean> =
fun Map<*, *>.requireBooleans(key: KProperty1<*, *>): Collection<Boolean> =
requireKey(key)

fun Map<*, *>.requireStrings(key: KProperty1<*, *>): List<String> =
fun Map<*, *>.requireStrings(key: KProperty1<*, *>): Collection<String> =
requireKey(key)

fun Map<*, *>.requireLists(key: KProperty1<*, *>): List<List<*>> =
fun Map<*, *>.requireLists(key: KProperty1<*, *>): Collection<Collection<*>> =
requireKey(key)

fun Map<*, *>.requireMaps(key: KProperty1<*, *>): List<Map<String, *>> =
fun Map<*, *>.requireMaps(key: KProperty1<*, *>): Collection<Map<String, *>> =
requireKey(key)
83 changes: 61 additions & 22 deletions core/src/test/kotlin/com/hexagonkt/core/DataTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,29 @@ internal class DataTest {
0 to 1
)

@Test fun `Maps are merged correctly`() {
assertEquals(mapOf("a" to true), merge(mapOf("a" to true), emptyMap<String, Boolean>()))
assertEquals(mapOf("a" to true), merge(emptyMap<String, Boolean>(), mapOf("a" to true)))
assertEquals(mapOf("a" to true), merge(mapOf("a" to false), mapOf("a" to true)))
assertEquals(mapOf("a" to true), merge(mapOf("a" to 1), mapOf("a" to true)))
assertEquals(mapOf("a" to 1, "b" to true), merge(mapOf("a" to 1), mapOf("b" to true)))
assertEquals(
mapOf("a" to listOf(1, 2, 3, 4)),
merge(mapOf("a" to listOf(1, 2)), mapOf("a" to listOf(3, 4)))
)
assertEquals(
mapOf("a" to listOf(1, 2, 3, 4, 3, 4)),
merge(mapOf("a" to listOf(1, 2, 3, 4)), mapOf("a" to listOf(3, 4)))
)
assertEquals(
mapOf("a" to mapOf("a" to 5, "b" to 6, "c" to 3, "d" to 4, "e" to 7, "f" to 8)),
merge(
mapOf("a" to mapOf("a" to 1, "b" to 2, "c" to 3, "d" to 4)),
mapOf("a" to mapOf("a" to 5, "b" to 6, "e" to 7, "f" to 8))
)
)
}

@Test fun `Get nested keys inside a map returns the proper value`() {
assert(m.keys<String>("nested", "zulu") == "charlie")
assert(m.keys<Any>("nested", "zulu", "tango") == null)
Expand Down Expand Up @@ -100,46 +123,46 @@ internal class DataTest {
}

@Test fun `Filtered maps do not contain empty elements`() {
assert(
assertEquals(
mapOf(
"a" to "b",
"b" to null,
"c" to 1,
"d" to listOf(1, 2),
"e" to listOf<String>(),
"f" to mapOf(0 to 1),
"g" to mapOf<String, Int>(),
"h" to mapOf("a" to true, "b" to null).filterEmpty(),
"i" to mapOf("a" to listOf<Int>()).filterEmpty()
).filterEmpty() ==
"h" to mapOf("a" to true),
),
mapOf(
"a" to "b",
"b" to null,
"c" to 1,
"d" to listOf(1, 2),
"e" to listOf<String>(),
"f" to mapOf(0 to 1),
"h" to mapOf("a" to true)
)
"g" to mapOf<String, Int>(),
"h" to mapOf("a" to true, "b" to null).filterNotEmpty(),
"i" to mapOf("a" to listOf<Int>()).filterNotEmpty()
).filterNotEmpty()
)
}

@Test fun `Filtered lists do not contain empty elements`() {
assert(
assertEquals(
listOf(
"a",
null,
listOf(1, 2),
listOf<String>(),
mapOf(0 to 1),
mapOf<String, Int>(),
mapOf("a" to true, "b" to null).filterEmpty(),
mapOf("a" to listOf<Int>()).filterEmpty()
).filterEmpty() ==
mapOf("a" to true)
),
listOf(
"a",
null,
listOf(1, 2),
listOf<String>(),
mapOf(0 to 1),
mapOf("a" to true)
)
mapOf<String, Int>(),
mapOf("a" to true, "b" to null).filterNotEmpty(),
mapOf("a" to listOf<Int>()).filterNotEmpty()
).filterNotEmpty()
)
}

Expand All @@ -163,8 +186,8 @@ internal class DataTest {
"e" to listOf<String>(),
"f" to mapOf(0 to 1),
"g" to mapOf<String, Int>(),
"h" to mapOf("a" to true, "b" to null).filterEmpty(),
"i" to mapOf("a" to listOf<Int>()).filterEmpty(),
"h" to mapOf("a" to true, "b" to null).filterNotEmpty(),
"i" to mapOf("a" to listOf<Int>()).filterNotEmpty(),
"j" to listOf(null, null),
"k" to mapOf("a" to null, "b" to null),
"l" to listOf(
Expand All @@ -177,7 +200,7 @@ internal class DataTest {
mapOf("a" to 1, "b" to "c", "z" to null),
null,
),
).filterEmptyRecursive()
).filterNotEmptyRecursive()
)
}

Expand All @@ -202,7 +225,23 @@ internal class DataTest {
assert(list.ensureSize(0..4) == list)
}

@Test fun `Utilities to map data objects works correctly`() {
@Test fun `Utilities to map not null values work correctly`() {
assertEquals(
mapOf(
"a" to 1,
"b" to true,
"c" to 'c',
),
mapOfNotNull(
"a" to 1,
"b" to true,
"c" to 'c',
"d" to null,
)
)
}

@Test fun `Utilities to map data objects work correctly`() {
data class DataClass(
val a: Int,
val b: Long,
Expand Down

0 comments on commit ecc60f1

Please sign in to comment.