Skip to content

Commit

Permalink
added reflective isArray functions (with tests), so we can treat arra…
Browse files Browse the repository at this point in the history
…ys as values in toDataFrame { properties() }
  • Loading branch information
Jolanrensen committed Apr 25, 2024
1 parent f3cc35c commit 2f79074
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 2 deletions.
103 changes: 103 additions & 0 deletions core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/TypeUtils.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@file:OptIn(ExperimentalUnsignedTypes::class)

package org.jetbrains.kotlinx.dataframe.impl

import org.jetbrains.kotlinx.dataframe.AnyFrame
Expand Down Expand Up @@ -467,3 +469,104 @@ internal fun nothingType(nullable: Boolean): KType =
} else {
typeOf<List<Nothing>>()
}.arguments.first().type!!

@OptIn(ExperimentalUnsignedTypes::class)
private val primitiveArrayClasses = setOf(
BooleanArray::class,
ByteArray::class,
ShortArray::class,
IntArray::class,
LongArray::class,
FloatArray::class,
DoubleArray::class,
CharArray::class,

UByteArray::class,
UShortArray::class,
UIntArray::class,
ULongArray::class,
)

/**
* Returns `true` if this class is a primitive array class like `XArray`.
*
* Use [KClass.isArray] to also check for `Array<>`.
*/
internal val KClass<*>.isPrimitiveArray: Boolean
get() = this in primitiveArrayClasses

/**
* Returns `true` if this class is an array, either a primitive array like `XArray` or `Array<>`.
*
* Use [KClass.isPrimitiveArray] to only check for primitive arrays.
*/
internal val KClass<*>.isArray: Boolean
get() = this in primitiveArrayClasses ||
this.qualifiedName == Array::class.qualifiedName // instance check fails

/**
* Returns `true` if this type is of a primitive array like `XArray`.
*
* Use [KType.isArray] to also check for `Array<>`.
*/
internal val KType.isPrimitiveArray: Boolean
get() =
if (arguments.isNotEmpty()) {
// Catching https://github.com/Kotlin/dataframe/issues/678
// as typeOf<Array<Int>>().classifier == IntArray::class
false
} else {
(classifier as? KClass<*>)?.isPrimitiveArray == true
}


/**
* Returns `true` if this type is of an array, either a primitive array like `XArray` or `Array<>`.
*
* Use [KType.isPrimitiveArray] to only check for primitive arrays.
*/
internal val KType.isArray: Boolean
get() = (classifier as? KClass<*>)?.isArray == true

/**
* Returns `true` if this object is a primitive array like `XArray`.
*
* Use [Any.isArray] to also check for the `Array<>` object.
*/
internal val Any.isPrimitiveArray: Boolean
get() = this::class.isPrimitiveArray

/**
* Returns `true` if this object is an array, either a primitive array like `XArray` or `Array<>`.
*
* Use [Any.isPrimitiveArray] to only check for primitive arrays.
*/
internal val Any.isArray: Boolean
get() = this::class.isArray

/**
* Returns this array object as a list of values.
*
* @throws IllegalArgumentException if this object is not an array
*/
internal fun Any.asArrayToList(): List<*> {
require(this.isArray) { "Not an array" }
return when (this) {
is BooleanArray -> toList()
is ByteArray -> toList()
is ShortArray -> toList()
is IntArray -> toList()
is LongArray -> toList()
is FloatArray -> toList()
is DoubleArray -> toList()
is CharArray -> toList()

is UByteArray -> toList()
is UShortArray -> toList()
is UIntArray -> toList()
is ULongArray -> toList()

is Array<*> -> toList()
else -> error("Not an array")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import org.jetbrains.kotlinx.dataframe.impl.asList
import org.jetbrains.kotlinx.dataframe.impl.columnName
import org.jetbrains.kotlinx.dataframe.impl.emptyPath
import org.jetbrains.kotlinx.dataframe.impl.getListType
import org.jetbrains.kotlinx.dataframe.impl.isArray
import org.jetbrains.kotlinx.dataframe.impl.isGetterLike
import org.jetbrains.kotlinx.dataframe.impl.projectUpTo
import org.jetbrains.kotlinx.dataframe.impl.schema.sortWithConstructors
Expand Down Expand Up @@ -51,7 +52,8 @@ internal val KClass<*>.isValueType: Boolean
this in valueTypes ||
this.isSubclassOf(Number::class) ||
this.isSubclassOf(Enum::class) ||
this.isSubclassOf(Temporal::class)
this.isSubclassOf(Temporal::class) ||
this.isArray

internal class CreateDataFrameDslImpl<T>(
override val source: Iterable<T>,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package org.jetbrains.kotlinx.dataframe.api

import io.kotest.matchers.shouldBe
import org.jetbrains.kotlinx.dataframe.DataColumn
import org.jetbrains.kotlinx.dataframe.DataFrame
import org.jetbrains.kotlinx.dataframe.DataRow
import org.jetbrains.kotlinx.dataframe.alsoDebug
import org.jetbrains.kotlinx.dataframe.annotations.DataSchema
import org.jetbrains.kotlinx.dataframe.columns.ColumnKind
import org.jetbrains.kotlinx.dataframe.kind
Expand Down Expand Up @@ -392,4 +392,19 @@ class CreateDataFrameTests {
properties(JavaPojo::getB)
} shouldBe dataFrameOf("b")("bb")
}

data class Arrays(val a: IntArray, val b: Array<Int>, val c: Array<Int?>)

@Test
fun `arrays in to DF`() {
val df = listOf(
Arrays(intArrayOf(1, 2), arrayOf(3, 4), arrayOf(5, null))
).toDataFrame(maxDepth = Int.MAX_VALUE)

df.schema() shouldBe dataFrameOf(
DataColumn.createValueColumn("a", listOf(intArrayOf(1, 2)), typeOf<IntArray>()),
DataColumn.createValueColumn("b", listOf(arrayOf(3, 4)), typeOf<Array<Int>>()),
DataColumn.createValueColumn("c", listOf(arrayOf(5, null)), typeOf<Array<Int?>>()),
).schema()
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package org.jetbrains.kotlinx.dataframe.types

import io.kotest.matchers.shouldBe
import org.jetbrains.kotlinx.dataframe.impl.asArrayToList
import org.jetbrains.kotlinx.dataframe.impl.commonParent
import org.jetbrains.kotlinx.dataframe.impl.commonParents
import org.jetbrains.kotlinx.dataframe.impl.commonType
import org.jetbrains.kotlinx.dataframe.impl.commonTypeListifyValues
import org.jetbrains.kotlinx.dataframe.impl.createType
import org.jetbrains.kotlinx.dataframe.impl.guessValueType
import org.jetbrains.kotlinx.dataframe.impl.isArray
import org.jetbrains.kotlinx.dataframe.impl.isPrimitiveArray
import org.jetbrains.kotlinx.dataframe.impl.nothingType
import org.jetbrains.kotlinx.dataframe.impl.replaceGenericTypeParametersWithUpperbound
import org.junit.Test
Expand All @@ -17,6 +20,55 @@ import kotlin.reflect.typeOf

class UtilTests {

@OptIn(ExperimentalUnsignedTypes::class)
@Test
fun `isArray tests`() {
// KClass isArray
BooleanArray::class.isArray shouldBe true
UIntArray::class.isArray shouldBe true
Array::class.isArray shouldBe true

// KClass isPrimitiveArray
BooleanArray::class.isPrimitiveArray shouldBe true
UIntArray::class.isPrimitiveArray shouldBe true
Array::class.isPrimitiveArray shouldBe false

// KType isArray
typeOf<BooleanArray>().isArray shouldBe true
typeOf<UIntArray>().isArray shouldBe true
typeOf<Array<Int>>().isArray shouldBe true
typeOf<Array<Int?>>().isArray shouldBe true
typeOf<Array<*>>().isArray shouldBe true

// KType isPrimitiveArray
typeOf<BooleanArray>().isPrimitiveArray shouldBe true
typeOf<UIntArray>().isPrimitiveArray shouldBe true
typeOf<Array<Int>>().isPrimitiveArray shouldBe false
typeOf<Array<Int?>>().isPrimitiveArray shouldBe false
typeOf<Array<*>>().isPrimitiveArray shouldBe false

// Any isArray
booleanArrayOf().isArray shouldBe true
uintArrayOf().isArray shouldBe true
arrayOf(1).isArray shouldBe true
arrayOf(1, null).isArray shouldBe true
arrayOfNulls<Any?>(1).isArray shouldBe true

// Any isPrimitiveArray
booleanArrayOf().isPrimitiveArray shouldBe true
uintArrayOf().isPrimitiveArray shouldBe true
arrayOf(1).isPrimitiveArray shouldBe false
arrayOf(1, null).isPrimitiveArray shouldBe false
arrayOfNulls<Any?>(1).isPrimitiveArray shouldBe false

// Any asArrayToList
booleanArrayOf(true, false).asArrayToList() shouldBe listOf(true, false)
uintArrayOf(1u, 2u).asArrayToList() shouldBe listOf(1u, 2u)
arrayOf(1, 2).asArrayToList() shouldBe listOf(1, 2)
arrayOf(1, null).asArrayToList() shouldBe listOf(1, null)
arrayOfNulls<Any?>(1).asArrayToList() shouldBe listOf(null)
}

@Test
fun commonParentsTests() {
commonParents(Int::class, Int::class) shouldBe listOf(Int::class)
Expand Down

0 comments on commit 2f79074

Please sign in to comment.