Skip to content

Commit bef47b5

Browse files
authored
Sort operation (#76)
* Basic Sort Operation * No defaults * More unit tests * Enum order * Proper enum usage * Enum outside the object * String list case added * TODO Add more cases * Some more unit tests * Boolean comparison + unit tests * , added * Suppression * Shorter name function * CR rename * Formatting * max line length fix * info flag to build workflow * more jvm memory * no info level in build
1 parent 383caf6 commit bef47b5

File tree

4 files changed

+236
-1
lines changed

4 files changed

+236
-1
lines changed

gradle.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ kotlin.code.style=official
22
kotlin.mpp.enableGranularSourceSetsMetadata=true
33
kotlin.native.enableDependencyPropagation=false
44
kotlin.mpp.stability.nowarn=true
5-
5+
org.gradle.jvmargs=-Xmx2560m
6+
org.gradle.daemon=true

operations-stdlib/src/commonMain/kotlin/OperationsProvider.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import array.Distinct
22
import array.Find
33
import array.Size
4+
import array.Sort
45
import operation.FunctionalLogicOperation
56
import operation.StandardLogicOperation
67
import string.Capitalize
@@ -25,6 +26,7 @@ object OperationsProvider {
2526

2627
// array
2728
"size" to Size,
29+
"sort" to Sort,
2830
"distinct" to Distinct,
2931

3032
"drop" to Drop,
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package array
2+
3+
import operation.StandardLogicOperation
4+
import utils.asDoubleList
5+
import utils.asList
6+
import utils.secondOrNull
7+
8+
object Sort : StandardLogicOperation {
9+
override fun evaluateLogic(expression: Any?, data: Any?): Any? =
10+
with(expression.asList) {
11+
val elementsToSort = firstOrNull()?.asList
12+
val sortingMode = (secondOrNull() as? String).toSortOrder()
13+
14+
elementsToSort?.sortByMode(sortingMode)
15+
}
16+
17+
private fun String?.toSortOrder() = when (this) {
18+
"desc" -> SortOrder.Descending
19+
"asc" -> SortOrder.Ascending
20+
else -> SortOrder.Unknown
21+
}
22+
23+
private fun List<Any?>.sortByMode(sortingMode: SortOrder) = when {
24+
containsOnlyElementsOfType<String>() -> this.castAndSortComparable<String>(sortingMode)
25+
containsOnlyElementsOfType<Boolean>() -> this.castAndSortComparable<Boolean>(sortingMode)
26+
containsOnlyElementsOfType<Number>() -> asDoubleList.filterNotNull().sortComparable(sortingMode)
27+
else -> null
28+
}
29+
30+
private inline fun <reified T> List<Any?>?.containsOnlyElementsOfType() =
31+
this?.filterIsInstance<T>()?.size == this?.size
32+
33+
@Suppress("UNCHECKED_CAST")
34+
private inline fun <reified T : Comparable<T>> List<*>.castAndSortComparable(sortingMode: SortOrder) =
35+
(this as? List<T>)?.sortComparable(sortingMode)
36+
37+
private inline fun <reified T : Comparable<T>> List<T>.sortComparable(sortingMode: SortOrder) =
38+
modeBasedSort(sortingMode = sortingMode, ascSort = { sorted() }, descSort = { sortedDescending() })
39+
40+
private fun modeBasedSort(sortingMode: SortOrder, ascSort: (() -> Any?), descSort: (() -> Any?)) =
41+
when (sortingMode) {
42+
SortOrder.Descending -> descSort()
43+
SortOrder.Ascending -> ascSort()
44+
SortOrder.Unknown -> null
45+
}
46+
}
47+
48+
private sealed class SortOrder {
49+
object Descending : SortOrder()
50+
object Ascending : SortOrder()
51+
object Unknown : SortOrder()
52+
}
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
package array
2+
3+
import JsonLogicEngine
4+
import JsonLogicResult.Failure
5+
import JsonLogicResult.Success
6+
import TestInput
7+
import io.kotest.core.spec.style.FunSpec
8+
import io.kotest.datatest.withData
9+
import io.kotest.matchers.shouldBe
10+
11+
class SortTest : FunSpec({
12+
val operatorName = "sort"
13+
val logicEngine = JsonLogicEngine.Builder().addStandardOperation(operatorName, Sort).build()
14+
15+
withData(
16+
nameFn = { input ->
17+
"Should evaluated $operatorName expression with given ${input.data} result in ${input.result}"
18+
},
19+
ts = listOf(
20+
TestInput(
21+
expression = mapOf(
22+
operatorName to listOf(
23+
mapOf(
24+
"map" to listOf(
25+
mapOf("var" to "integers", "var" to "integers"),
26+
mapOf("%" to listOf(mapOf("var" to ""), 3)),
27+
mapOf("var" to "integers", "var" to "integers"),
28+
)
29+
), "desc"
30+
)
31+
),
32+
data = mapOf("integers" to listOf(1, 2, 3, 4, 5)),
33+
result = Success(listOf(2.0, 2.0, 1.0, 1.0, 0.0))
34+
),
35+
TestInput(
36+
expression = mapOf(
37+
operatorName to listOf(
38+
mapOf(
39+
"map" to listOf(
40+
mapOf("var" to "integers"),
41+
mapOf("%" to listOf(mapOf("var" to ""), 2))
42+
)
43+
), "asc"
44+
)
45+
),
46+
data = mapOf("integers" to listOf(1, 2, 3, 4, 5)),
47+
result = Success(listOf(0.0, 0.0, 1.0, 1.0, 1.0))
48+
),
49+
TestInput(
50+
expression = mapOf(operatorName to listOf(listOf(1, 2, 3))),
51+
result = Failure.NullResult
52+
),
53+
TestInput(
54+
expression = mapOf(operatorName to listOf(listOf(1.0, 2, 3.5), "desc")),
55+
result = Success(listOf(3.5, 2.0, 1.0))
56+
),
57+
TestInput(
58+
expression = mapOf(operatorName to listOf(0, "desc")),
59+
result = Success(listOf(0.0))
60+
),
61+
TestInput(
62+
expression = mapOf(operatorName to listOf(listOf(0.01, 0.01, 0.001), "desc")),
63+
result = Success(listOf(0.01, 0.01, 0.001))
64+
),
65+
TestInput(
66+
expression = mapOf(operatorName to listOf(listOf("0.1", "0.01", "0.001"), "desc")),
67+
result = Success(listOf("0.1", "0.01", "0.001"))
68+
),
69+
TestInput(
70+
expression = mapOf(operatorName to listOf(listOf(1, "true", 3), "asc")),
71+
result = Failure.NullResult
72+
),
73+
TestInput(
74+
expression = mapOf(operatorName to listOf(listOf(1, 3, null), "asc")),
75+
result = Failure.NullResult
76+
),
77+
TestInput(
78+
expression = mapOf(operatorName to listOf(listOf(1, 2), listOf(3, 4), "asc")),
79+
result = Failure.NullResult
80+
),
81+
TestInput(
82+
expression = mapOf(operatorName to listOf(listOf(1, "2", 3), "asc")),
83+
result = Failure.NullResult
84+
),
85+
TestInput(
86+
expression = mapOf(operatorName to listOf(listOf(1, 2, 3), "asc")),
87+
result = Success(listOf(1.0, 2.0, 3.0))
88+
),
89+
TestInput(
90+
expression = mapOf(operatorName to listOf(listOf(1, 2, 3), "desc")),
91+
result = Success(listOf(3.0, 2.0, 1.0))
92+
),
93+
TestInput(
94+
expression = mapOf(operatorName to "banana"),
95+
result = Failure.NullResult
96+
),
97+
TestInput(
98+
expression = mapOf(operatorName to listOf(listOf("banana", "apple", "strawberry"), "asc")),
99+
result = Success(listOf("apple", "banana", "strawberry"))
100+
),
101+
TestInput(
102+
expression = mapOf(operatorName to listOf(listOf("banana", 2, "strawberry"), "asc")),
103+
result = Failure.NullResult
104+
),
105+
TestInput(
106+
expression = mapOf(operatorName to listOf(listOf("banana", null, "strawberry"), "asc")),
107+
result = Failure.NullResult
108+
),
109+
TestInput(
110+
expression = mapOf(operatorName to null),
111+
result = Failure.NullResult
112+
),
113+
TestInput(
114+
expression = mapOf(operatorName to true),
115+
result = Failure.NullResult
116+
),
117+
TestInput(
118+
expression = mapOf(operatorName to emptyList<Any>()),
119+
result = Failure.NullResult
120+
),
121+
TestInput(
122+
expression = mapOf(operatorName to listOf(null, mapOf("var" to ""))),
123+
result = Failure.NullResult
124+
),
125+
TestInput(
126+
expression = mapOf(operatorName to listOf(emptyList<String>(), mapOf("var" to ""))),
127+
result = Failure.NullResult
128+
),
129+
TestInput(
130+
expression = mapOf(operatorName to listOf(listOf(false, false), mapOf("var" to ""))),
131+
result = Failure.NullResult
132+
),
133+
TestInput(
134+
expression = mapOf(operatorName to listOf(listOf(true, false), "asc")),
135+
result = Success(listOf(false, true))
136+
),
137+
TestInput(
138+
expression = mapOf(operatorName to listOf(listOf(true), "asc")),
139+
result = Success(listOf(true))
140+
),
141+
TestInput(
142+
expression = mapOf(operatorName to listOf(listOf(false), "asc")),
143+
result = Success(listOf(false))
144+
),
145+
TestInput(
146+
expression = mapOf(operatorName to listOf(listOf(true, null), "asc")),
147+
result = Failure.NullResult
148+
),
149+
TestInput(
150+
expression = mapOf(operatorName to listOf(listOf(true, "1"), "asc")),
151+
result = Failure.NullResult
152+
),
153+
TestInput(
154+
expression = mapOf(operatorName to listOf(listOf(true, "true"), "asc")),
155+
result = Failure.NullResult
156+
),
157+
TestInput(
158+
expression = mapOf(operatorName to listOf(listOf(true, false), "desc")),
159+
result = Success(listOf(true, false))
160+
),
161+
TestInput(
162+
expression = mapOf(
163+
operatorName to listOf(
164+
mapOf("var" to "fruits"),
165+
mapOf("==" to listOf(mapOf("var" to ""), "pineapple"))
166+
)
167+
),
168+
data = mapOf("fruits" to listOf("apple", "banana", "pineapple")),
169+
result = Failure.NullResult
170+
),
171+
)
172+
// given
173+
) { testInput: TestInput ->
174+
// when
175+
val evaluationResult = logicEngine.evaluate(testInput.expression, testInput.data)
176+
177+
// then
178+
evaluationResult shouldBe testInput.result
179+
}
180+
})

0 commit comments

Comments
 (0)