All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.space.kscience.dataforge.data.dataTransform.kt Maven / Gradle / Ivy

There is a newer version: 0.7.0
Show newest version
package space.kscience.dataforge.data

import kotlinx.coroutines.flow.*
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.seal
import space.kscience.dataforge.meta.toMutableMeta
import space.kscience.dataforge.misc.DFInternal
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.reflect.KType
import kotlin.reflect.typeOf

/**
 * Lazily transform this data to another data. By convention [block] should not use external data (be pure).
 * @param coroutineContext additional [CoroutineContext] elements used for data computation.
 * @param meta for the resulting data. By default equals input data.
 * @param block the transformation itself
 */
public inline fun  Data.map(
    coroutineContext: CoroutineContext = EmptyCoroutineContext,
    meta: Meta = this.meta,
    crossinline block: suspend (T) -> R,
): Data = Data(meta, coroutineContext, listOf(this)) {
    block(await())
}

/**
 * Combine this data with the other data using [block]. See [map] for other details
 */
public inline fun  Data.combine(
    other: Data,
    coroutineContext: CoroutineContext = EmptyCoroutineContext,
    meta: Meta = this.meta,
    crossinline block: suspend (left: T1, right: T2) -> R,
): Data = Data(meta, coroutineContext, listOf(this, other)) {
    block(await(), other.await())
}


//data collection operations

/**
 * Lazily reduce a collection of [Data] to a single data.
 */
public inline fun  Collection>.reduceToData(
    coroutineContext: CoroutineContext = EmptyCoroutineContext,
    meta: Meta = Meta.EMPTY,
    crossinline block: suspend (Collection) -> R,
): Data = Data(
    meta,
    coroutineContext,
    this
) {
    block(map { it.await() })
}

@DFInternal
public fun  Map>.reduceToData(
    outputType: KType,
    coroutineContext: CoroutineContext = EmptyCoroutineContext,
    meta: Meta = Meta.EMPTY,
    block: suspend (Map) -> R,
): Data = Data(
    outputType,
    meta,
    coroutineContext,
    this.values
) {
    block(mapValues { it.value.await() })
}


/**
 * Lazily reduce a [Map] of [Data] with any static key.
 * @param K type of the map key
 * @param T type of the input goal
 * @param R type of the result goal
 */
public inline fun  Map>.reduceToData(
    coroutineContext: CoroutineContext = EmptyCoroutineContext,
    meta: Meta = Meta.EMPTY,
    noinline block: suspend (Map) -> R,
): Data = Data(
    meta,
    coroutineContext,
    this.values
) {
    block(mapValues { it.value.await() })
}

//flow operations

/**
 * Transform a [Flow] of [NamedData] to a single [Data].
 */
@DFInternal
public suspend fun  Flow>.reduceToData(
    outputType: KType,
    coroutineContext: CoroutineContext = EmptyCoroutineContext,
    meta: Meta = Meta.EMPTY,
    transformation: suspend (Flow>) -> R,
): Data = Data(
    outputType,
    meta,
    coroutineContext,
    toList()
) {
    transformation(this)
}

@OptIn(DFInternal::class)
public suspend inline fun  Flow>.reduceToData(
    coroutineContext: CoroutineContext = EmptyCoroutineContext,
    meta: Meta = Meta.EMPTY,
    noinline transformation: suspend (Flow>) -> R,
): Data = reduceToData(typeOf(), coroutineContext, meta) {
    transformation(it)
}

/**
 * Fold a flow of named data into a single [Data]
 */
public suspend inline fun  Flow>.foldToData(
    initial: R,
    coroutineContext: CoroutineContext = EmptyCoroutineContext,
    meta: Meta = Meta.EMPTY,
    noinline block: suspend (result: R, data: NamedData) -> R,
): Data = reduceToData(
    coroutineContext, meta
) {
    it.fold(initial, block)
}

//DataSet operations

@DFInternal
public suspend fun  DataSet.map(
    outputType: KType,
    coroutineContext: CoroutineContext = EmptyCoroutineContext,
    metaTransform: MutableMeta.() -> Unit = {},
    block: suspend (T) -> R,
): DataTree = DataTree(outputType) {
    populate(
        flow().map {
            val newMeta = it.meta.toMutableMeta().apply(metaTransform).seal()
            Data(outputType, newMeta, coroutineContext, listOf(it)) {
                block(it.await())
            }.named(it.name)
        }
    )
}

@OptIn(DFInternal::class)
public suspend inline fun  DataSet.map(
    coroutineContext: CoroutineContext = EmptyCoroutineContext,
    noinline metaTransform: MutableMeta.() -> Unit = {},
    noinline block: suspend (T) -> R,
): DataTree = map(typeOf(), coroutineContext, metaTransform, block)

public suspend fun  DataSet.forEach(block: suspend (NamedData) -> Unit) {
    contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
    flow().collect {
        block(it)
    }
}

public suspend inline fun  DataSet.reduceToData(
    coroutineContext: CoroutineContext = EmptyCoroutineContext,
    meta: Meta = Meta.EMPTY,
    noinline transformation: suspend (Flow>) -> R,
): Data = flow().reduceToData(coroutineContext, meta, transformation)

public suspend inline fun  DataSet.foldToData(
    initial: R,
    coroutineContext: CoroutineContext = EmptyCoroutineContext,
    meta: Meta = Meta.EMPTY,
    noinline block: suspend (result: R, data: NamedData) -> R,
): Data = flow().foldToData(initial, coroutineContext, meta, block)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy