Table refactoring

This commit is contained in:
Alexander Nozik 2020-06-24 14:40:55 +03:00
parent 73fbe19fed
commit 059ade7973
12 changed files with 236 additions and 97 deletions

View File

@ -7,7 +7,7 @@ plugins {
id("org.jetbrains.dokka") version "0.10.1"
}
val dataforgeVersion by extra("0.1.8-dev-4")
val dataforgeVersion by extra("0.1.8-dev-5")
val bintrayRepo by extra("dataforge")
val githubProject by extra("dataforge-core")

View File

@ -183,6 +183,11 @@ fun Configurable.stringList(vararg strings: String, key: Name? = null): ReadWrit
it?.value?.stringList ?: emptyList()
}
fun Configurable.stringListOrNull(vararg strings: String, key: Name? = null): ReadWriteProperty<Any?, List<String>?> =
item(listOf(*strings), key) {
it?.value?.stringList
}
fun Configurable.numberList(vararg numbers: Number, key: Name? = null): ReadWriteProperty<Any?, List<Number>> =
item(listOf(*numbers), key) { item ->
item?.value?.list?.map { it.number } ?: emptyList()

View File

@ -1,28 +1,23 @@
package hep.dataforge.tables
import kotlin.reflect.KClass
/**
* @param C bottom type for all columns in the table
* @param T bottom type for all columns in the table
*/
class ColumnTable<C : Any>(override val columns: Collection<Column<C>>) : Table<C> {
class ColumnTable<T : Any>(override val columns: Collection<Column<T>>) : Table<T> {
private val rowsNum = columns.first().size
init {
require(columns.all { it.size == rowsNum }) { "All columns must be of the same size" }
}
override val rows: List<Row<C>>
override val rows: List<Row<T>>
get() = (0 until rowsNum).map { VirtualRow(this, it) }
override fun <T : C> getValue(row: Int, column: String, type: KClass<out T>): T? {
val value = columns[column]?.get(row)
return type.cast(value)
}
override fun getValue(row: Int, column: String): T? = columns[column]?.get(row)
}
internal class VirtualRow<C : Any>(val table: Table<C>, val index: Int) : Row<C> {
override fun <T : C> getValue(column: String, type: KClass<out T>): T? = table.getValue(index, column, type)
internal class VirtualRow<T : Any>(val table: Table<T>, val index: Int) : Row<T> {
override fun getValue(column: String): T? = table.getValue(index, column)
// override fun <T : C> get(columnHeader: ColumnHeader<T>): T? {
// return table.co[columnHeader][index]

View File

@ -1,10 +0,0 @@
package hep.dataforge.tables
import kotlin.reflect.KClass
inline class MapRow<C: Any>(val values: Map<String, C?>) : Row<C> {
override fun <T : C> getValue(column: String, type: KClass<out T>): T? {
val value = values[column]
return type.cast(value)
}
}

View File

@ -1,8 +1,5 @@
package hep.dataforge.tables
import hep.dataforge.meta.Meta
import kotlin.reflect.KClass
/**
* Mutable table with a fixed size, but dynamic columns
*/
@ -14,10 +11,7 @@ class MutableColumnTable<C: Any>(val size: Int) : Table<C> {
VirtualRow(this, it)
}
override fun <T : C> getValue(row: Int, column: String, type: KClass<out T>): T? {
val value = columns[column]?.get(row)
return type.cast(value)
}
override fun getValue(row: Int, column: String): C? = columns[column]?.get(row)
/**
* Add a fixed column to the end of the table
@ -35,27 +29,3 @@ class MutableColumnTable<C: Any>(val size: Int) : Table<C> {
_columns.add(index, column)
}
}
class MapColumn<T : Any, R : Any>(
val source: Column<T>,
override val type: KClass<out R>,
override val name: String,
override val meta: Meta = source.meta,
val mapper: (T?) -> R?
) : Column<R> {
override val size: Int get() = source.size
override fun get(index: Int): R? = mapper(source[index])
}
class CachedMapColumn<T : Any, R : Any>(
val source: Column<T>,
override val type: KClass<out R>,
override val name: String,
override val meta: Meta = source.meta,
val mapper: (T?) -> R?
) : Column<R> {
override val size: Int get() = source.size
private val values: HashMap<Int, R?> = HashMap()
override fun get(index: Int): R? = values.getOrPut(index) { mapper(source[index]) }
}

View File

@ -9,7 +9,7 @@ class MutableTable<C : Any>(
override val header: MutableList<ColumnHeader<C>>
) : RowTable<C>(rows, header) {
fun <T : C> column(name: String, type: KClass<out T>, meta: Meta): ColumnHeader<T> {
fun <R : C> column(name: String, type: KClass<out R>, meta: Meta): ColumnHeader<R> {
val column = SimpleColumnHeader(name, type, meta)
header.add(column)
return column

View File

@ -4,6 +4,10 @@ import hep.dataforge.meta.Meta
import kotlinx.coroutines.flow.toList
import kotlin.reflect.KClass
inline class MapRow<C : Any>(val values: Map<String, C?>) : Row<C> {
override fun getValue(column: String): C? = values[column]
}
internal class RowTableColumn<C : Any, T : C>(val table: Table<C>, val header: ColumnHeader<T>) : Column<T> {
override val name: String get() = header.name
override val type: KClass<out T> get() = header.type
@ -14,8 +18,7 @@ internal class RowTableColumn<C : Any, T : C>(val table: Table<C>, val header: C
}
open class RowTable<C : Any>(override val rows: List<Row<C>>, override val header: List<ColumnHeader<C>>) : Table<C> {
override fun <T : C> getValue(row: Int, column: String, type: KClass<out T>): T? =
rows[row].getValue(column, type)
override fun getValue(row: Int, column: String): C? = rows[row].getValue(column)
override val columns: List<Column<C>> get() = header.map { RowTableColumn(this, it) }
}

View File

@ -3,32 +3,23 @@ package hep.dataforge.tables
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import kotlin.reflect.KClass
//TODO to be removed in 1.3.70
@Suppress("UNCHECKED_CAST")
internal fun <T : Any> KClass<T>.cast(value: Any?): T? {
return when {
value == null -> null
!isInstance(value) -> error("Expected type is $this, but found ${value::class}")
else -> value as T
}
}
import kotlin.reflect.cast
/**
* Finite or infinite row set. Rows are produced in a lazy suspendable [Flow].
* Each row must contain at least all the fields mentioned in [header].
*/
interface Rows<C : Any> {
val header: TableHeader<C>
fun rowFlow(): Flow<Row<C>>
interface Rows<out T : Any> {
val header: TableHeader<T>
fun rowFlow(): Flow<Row<T>>
}
interface Table<C : Any> : Rows<C> {
fun <T : C> getValue(row: Int, column: String, type: KClass<out T>): T?
val columns: Collection<Column<C>>
override val header: TableHeader<C> get() = columns.toList()
val rows: List<Row<C>>
override fun rowFlow(): Flow<Row<C>> = rows.asFlow()
interface Table<out T : Any> : Rows<T> {
fun getValue(row: Int, column: String): T?
val columns: Collection<Column<T>>
override val header: TableHeader<T> get() = columns.toList()
val rows: List<Row<T>>
override fun rowFlow(): Flow<Row<T>> = rows.asFlow()
/**
* Apply typed query to this table and return lazy [Flow] of resulting rows. The flow could be empty.
@ -40,14 +31,18 @@ interface Table<C : Any> : Rows<C> {
}
}
operator fun Collection<Column<*>>.get(name: String): Column<*>? = find { it.name == name }
fun <C : Any, T : C> Table<C>.getValue(row: Int, column: String, type: KClass<out T>): T? =
type.cast(getValue(row, column))
operator fun <T : Any> Collection<Column<T>>.get(name: String): Column<T>? = find { it.name == name }
inline operator fun <C : Any, reified T : C> Table<C>.get(row: Int, column: String): T? =
getValue(row, column, T::class)
operator fun <C : Any, T : C> Table<C>.get(row: Int, column: ColumnHeader<T>): T? = getValue(row, column.name, column.type)
operator fun <C : Any, T : C> Table<C>.get(row: Int, column: ColumnHeader<T>): T? =
getValue(row, column.name, column.type)
interface Column<T : Any> : ColumnHeader<T> {
interface Column<out T : Any> : ColumnHeader<T> {
val size: Int
operator fun get(index: Int): T?
}
@ -60,9 +55,11 @@ operator fun <T : Any> Column<T>.iterator() = iterator {
}
}
interface Row<C: Any> {
fun <T : C> getValue(column: String, type: KClass<out T>): T?
interface Row<out T : Any> {
fun getValue(column: String): T?
}
inline operator fun <C : Any, reified T : C> Row<C>.get(column: String): T? = getValue(column, T::class)
fun <C : Any, T : C> Row<C>.getValue(column: String, type: KClass<out T>): T? = type.cast(getValue(column))
inline operator fun <reified T : Any> Row<*>.get(column: String): T? = T::class.cast(getValue(column))
operator fun <C : Any, T : C> Row<C>.get(column: ColumnHeader<T>): T? = getValue(column.name, column.type)

View File

@ -0,0 +1,60 @@
package hep.dataforge.tables
import hep.dataforge.meta.Meta
import kotlin.reflect.KClass
/**
* A virtual column obtained by transforming Given row to a single value
*/
class TransformationColumn<T : Any, R : Any>(
val table: Table<T>,
override val type: KClass<out R>,
override val name: String,
override val meta: Meta,
val mapper: (Row<T>) -> R?
) : Column<R> {
override val size: Int get() = table.rows.size
override fun get(index: Int): R? = mapper(table.rows[index])
}
/**
* A virtual column obtained via transformation of single column with caching results on call (evaluation is lazy).
*
* Calls are not thread safe
*/
class CachedTransformationColumn<T : Any, R : Any>(
val table: Table<T>,
override val type: KClass<out R>,
override val name: String,
override val meta: Meta,
val mapper: (Row<T>) -> R?
) : Column<R> {
override val size: Int get() = table.rows.size
private val values: HashMap<Int, R?> = HashMap()
override fun get(index: Int): R? = values.getOrPut(index) { mapper(table.rows[index]) }
}
/**
* Create a virtual column from a given column
*/
inline fun <T : Any, reified R : Any> Table<T>.mapRows(
name: String,
meta: Meta = Meta.EMPTY,
cache: Boolean = false,
noinline mapper: (Row<T>) -> R?
): Column<R> = if (cache) {
CachedTransformationColumn(this, R::class, name, meta, mapper)
} else {
TransformationColumn(this, R::class, name, meta, mapper)
}
fun <T : Any> Table<T>.mapRowsToDouble(name: String, meta: Meta = Meta.EMPTY, block: (Row<T>) -> Double): RealColumn {
val data = DoubleArray(rows.size) { block(rows[it]) }
return RealColumn(name, data, meta)
}
fun <T : Any> Table<T>.mapRowsToInt(name: String, meta: Meta = Meta.EMPTY, block: (Row<T>) -> Int): IntColumn {
val data = IntArray(rows.size) { block(rows[it]) }
return IntColumn(name, data, meta)
}

View File

@ -13,7 +13,6 @@ import kotlinx.io.text.forEachUtf8Line
import kotlinx.io.text.readUtf8Line
import kotlinx.io.text.readUtf8StringUntilDelimiter
import kotlinx.io.text.writeUtf8String
import kotlin.reflect.KClass
/**
* Read a lin as a fixed width [Row]
@ -92,9 +91,9 @@ class TextTable(
}
}
override fun <T : Value> getValue(row: Int, column: String, type: KClass<out T>): T? {
override fun getValue(row: Int, column: String): Value? {
val offset = index[row]
return type.cast(readAt(offset)[column])
return readAt(offset)[column]
}
companion object {

View File

@ -3,9 +3,8 @@ package hep.dataforge.tables
import hep.dataforge.meta.Meta
import kotlin.reflect.KClass
//interface NumberColumn<N : Number> : Column<N>
data class RealColumn(
class RealColumn(
override val name: String,
val data: DoubleArray,
override val meta: Meta = Meta.EMPTY
@ -44,12 +43,7 @@ data class RealColumn(
}
}
fun <T : Any> Column<T>.map(meta: Meta = this.meta, block: (T?) -> Double): RealColumn {
val data = DoubleArray(size) { block(get(it)) }
return RealColumn(name, data, meta)
}
data class IntColumn(
class IntColumn(
override val name: String,
val data: IntArray,
override val meta: Meta = Meta.EMPTY
@ -86,9 +80,4 @@ data class IntColumn(
noinline metaBuilder: ColumnScheme.() -> Unit
): IntColumn = IntColumn(name, data, ColumnScheme(metaBuilder).toMeta())
}
}
fun <T : Any> Column<T>.map(meta: Meta = this.meta, block: (T?) -> Int): IntColumn {
val data = IntArray(size) { block(get(it)) }
return IntColumn(name, data, meta)
}

131
docs/images/df.svg Normal file
View File

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
viewBox="0 0 744.09448819 1052.3622047"
id="svg3570"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="df_3.svg">
<defs
id="defs3572">
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath134">
<path
d="m 0,595.28 841.89,0 L 841.89,0 0,0 0,595.28 Z"
id="path136"
inkscape:connector-curvature="0" />
</clipPath>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.979899"
inkscape:cx="290.99791"
inkscape:cy="622.26982"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1018"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata3575">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g46"
transform="matrix(1.25,0,0,-1.25,447.15562,475.52655)"
inkscape:export-xdpi="299.41061"
inkscape:export-ydpi="299.41061">
<path
d="m 0,0 0,16.432 c -7.415,0.038 -12.81,2.792 -14.994,9.821 -2,6.436 0.476,11.31 8.212,16.808 -2.372,4.23 -4.767,8.502 -7.287,12.996 -6.34,-4.254 -12.473,-4.711 -17.872,0.18 -6.348,5.75 -5.782,12.569 -1.036,19.595 -4.983,2.94 -9.579,5.652 -14.504,8.558 -3.428,-7.01 -8.655,-10.518 -15.95,-8.816 -8.161,1.906 -10.745,8.234 -10.582,16.105 l -16.599,0 c 0.4,-7.676 -2.268,-13.46 -9.793,-15.509 -7.719,-2.101 -13.026,1.654 -16.73,8.84 -4.456,-2.528 -8.687,-4.93 -12.927,-7.336 4.661,-7.839 4.704,-13.692 -0.087,-18.435 -1.862,-1.844 -4.611,-3.8 -7.011,-3.873 -4.26,-0.128 -8.561,1.142 -13.704,1.965 -2.108,-3.527 -4.849,-8.114 -7.734,-12.94 7.167,-3.404 11.145,-8.53 9.409,-16.072 -1.924,-8.364 -8.313,-11.09 -16.737,-10.521 l 0,-16.238 c 10.187,-0.896 14.702,-3.561 16.078,-10.002 0.551,-2.58 0.817,-5.899 -0.354,-8.015 -1.955,-3.534 -5.169,-6.371 -8.335,-10.065 1.904,-3.356 4.318,-7.609 6.714,-11.829 8.429,4.235 14.172,3.834 18.801,-0.978 4.637,-4.82 4.883,-10.766 0.327,-19.009 4.859,-2.85 9.546,-5.599 14.193,-8.326 5.229,7.735 10.197,10.412 16.46,8.681 7.893,-2.182 10.451,-8.222 10.32,-15.979 l 16.681,0 c -0.482,7.551 2.121,13.044 9.165,15.339 8.143,2.654 13.424,-1.565 17.358,-8.607 4.381,2.493 8.538,4.859 12.816,7.294 -3.79,6.509 -4.092,12.616 1.096,17.873 5.58,5.655 12.009,5.168 18.663,0.894 2.977,5.01 5.765,9.702 8.619,14.505 -6.371,3.744 -9.773,8.633 -8.323,15.701 C -14.296,-4.379 -9.626,-1.19 0,0"
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path48"
inkscape:connector-curvature="0" />
</g>
<g
id="g50"
transform="matrix(1.25,0,0,-1.25,343.65424,533.29343)"
inkscape:export-xdpi="299.41061"
inkscape:export-ydpi="299.41061">
<path
d="m 0,0 c -29.975,0 -54.362,24.387 -54.362,54.362 0,29.976 24.387,54.363 54.362,54.363 29.975,0 54.362,-24.387 54.362,-54.363 C 54.362,24.387 29.975,0 0,0"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path52"
inkscape:connector-curvature="0" />
</g>
<g
id="g54"
transform="matrix(1.25,0,0,-1.25,343.65424,404.40267)"
inkscape:export-xdpi="299.41061"
inkscape:export-ydpi="299.41061">
<path
d="m 0,0 c -26.881,0 -48.75,-21.869 -48.75,-48.75 0,-26.881 21.869,-48.75 48.75,-48.75 26.881,0 48.75,21.869 48.75,48.75 C 48.75,-21.869 26.881,0 0,0"
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path56"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(1.25,0,0,-1.25,-100.98176,778.91455)"
clip-path="url(#clipPath134)"
id="g132"
inkscape:export-xdpi="299.41061"
inkscape:export-ydpi="299.41061">
<g
transform="translate(355.3533,210.288)"
id="g138">
<path
inkscape:connector-curvature="0"
id="path140"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -22.83,0.215 -41.145,18.741 -40.964,41.436 0.183,22.8 18.723,41.22 41.357,41.089 C 23.53,82.392 41.585,63.584 41.363,39.847 41.16,18.168 22.085,-0.208 0,0" />
</g>
<g
transform="translate(339.7856,261.6331)"
id="g142">
<path
inkscape:connector-curvature="0"
id="path144"
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 -3.934,-13.762 7.014,0 L 7.014,0 0,0 Z m 15.961,24.284 5.261,0 c 1.002,0 1.376,-1.026 1.124,-3.076 L 11.562,-16.514 c -0.9,-2.051 -1.902,-3.076 -3.008,-3.076 l -13.231,0.162 -8.142,0 c -1.044,0 -1.45,0.917 -1.217,2.753 L -9.5,-0.81 c 2.289,3.994 4.531,5.991 6.723,5.991 l 11.272,0 4.721,16.513 c 0.87,1.726 1.785,2.59 2.745,2.59" />
</g>
<g
transform="translate(365.52,266.6515)"
id="g146">
<path
inkscape:connector-curvature="0"
id="path148"
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 18.538,0 c 1.35,-0.243 1.797,-1.161 1.342,-2.753 l -2.175,-7.608 c -0.679,-2.374 -2.062,-3.561 -4.149,-3.561 l -3.007,0 c -2.087,0 -2.838,1.024 -2.252,3.075 l 1.62,5.666 -7.014,0 -3.935,-13.761 4.886,0 c 1.356,0 1.815,-1.025 1.375,-3.076 -0.378,-1.321 -1.377,-2.185 -2.995,-2.59 l -4.886,0 -4.674,-16.351 c -0.868,-1.943 -1.815,-2.915 -2.837,-2.915 l -5.01,0 c -1.403,0.351 -1.877,1.323 -1.422,2.915 l 9.998,34.968 C -4.287,-1.997 -2.088,0 0,0" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.4 KiB