Skip to content

Commit 5c54f58

Browse files
committed
implicit conversion from Char to String in DataFrame.convertTo<X> { parser { ... } }
1 parent f86a6cc commit 5c54f58

File tree

3 files changed

+55
-2
lines changed

3 files changed

+55
-2
lines changed

core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/convertTo.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public class ConverterScope(public val fromType: KType, public val toSchema: Col
5252
* df.convertTo<SomeSchema> {
5353
* // defines how to convert Int? -> String
5454
* convert<Int?>().with { it?.toString() ?: "No input given" }
55-
* // defines how to convert String -> SomeType
55+
* // defines how to convert String -> SomeType (and Char.toString() -> SomeType)
5656
* parser { SomeType(it) }
5757
* // fill missing column `sum` with expression `a+b`
5858
* fill { sum }.with { a + b }
@@ -102,6 +102,10 @@ public fun <T, C> ConvertToFill<T, C>.with(expr: RowExpression<T, C>) {
102102

103103
/**
104104
* Defines how to convert `String` values into given type [C].
105+
*
106+
* This method is a shortcut for `convert<String>().with { }`.
107+
*
108+
* If no converter is defined for `Char` values, this converter will be used for them as well.
105109
*/
106110
public inline fun <reified C> ConvertSchemaDsl<*>.parser(noinline parser: (String) -> C): Unit =
107111
convert<String>().with(parser)

core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/convertTo.kt

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ import org.jetbrains.kotlinx.dataframe.schema.ColumnSchema
4545
import org.jetbrains.kotlinx.dataframe.schema.DataFrameSchema
4646
import org.jetbrains.kotlinx.dataframe.size
4747
import kotlin.reflect.KType
48+
import kotlin.reflect.full.isSubtypeOf
4849
import kotlin.reflect.full.withNullability
4950
import kotlin.reflect.jvm.jvmErasure
51+
import kotlin.reflect.typeOf
5052

5153
private val logger = KotlinLogging.logger {}
5254

@@ -143,7 +145,22 @@ internal fun AnyFrame.convertToImpl(
143145
// try to perform any user-specified conversions first
144146
val from = originalColumn.type()
145147
val to = targetSchema.type
146-
val converter = dsl.getConverter(from, targetSchema)
148+
var converter = dsl.getConverter(from, targetSchema)
149+
150+
// special case for Char columns; check if we have any converters for String -> target
151+
// if so, we can convert Char -> String -> target
152+
if (converter == null && from.isSubtypeOf(typeOf<Char?>())) {
153+
val stringConverter = dsl.getConverter(
154+
fromType = typeOf<String>().withNullability(from.isMarkedNullable),
155+
toSchema = targetSchema,
156+
)
157+
if (stringConverter != null) {
158+
converter = Converter(
159+
transform = { stringConverter.transform(this, (it as Char?)?.toString()) },
160+
skipNulls = stringConverter.skipNulls,
161+
)
162+
}
163+
}
147164

148165
val convertedColumn = if (converter != null) {
149166
val nullsAllowed = to.isMarkedNullable

core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/convertTo.kt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,38 @@ class ConvertToTests {
5151
df.convertTo<Schema> { parser { A(it.toInt()) } }
5252
.single()
5353
.a.value shouldBe 1
54+
55+
// shortcut for:
56+
df.convertTo<Schema> { convert<String>().with { A(it.toInt()) } }
57+
.single()
58+
.a.value shouldBe 1
59+
}
60+
61+
@Test
62+
fun `convert from char with parser`() {
63+
val df = dataFrameOf("a")('1')
64+
65+
shouldThrow<TypeConverterNotFoundException> {
66+
df.convertTo<Schema>()
67+
}
68+
69+
// Char -> String -> Target
70+
df.convertTo<Schema> { parser { A(it.toInt()) } }
71+
.single()
72+
.a.value shouldBe 1
73+
74+
// shortcut for:
75+
df.convertTo<Schema> { convert<String>().with { A(it.toInt()) } }
76+
.single()
77+
.a.value shouldBe 1
78+
79+
// Char -> Target
80+
df.convertTo<Schema> {
81+
parser<A> { error("should not be triggered if convert<Char>() is present") }
82+
convert<String>().with<_, A> { error("should not be triggered if convert<Char>() is present") }
83+
84+
convert<Char>().with { A(it.digitToInt()) }
85+
}.single().a.value shouldBe 1
5486
}
5587

5688
@Test

0 commit comments

Comments
 (0)