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

commonMain.validations.Uniques.kt Maven / Gradle / Ivy

@file:OptIn(ExperimentalStdlibApi::class)

package pt.lightweightform.lfkotlin.validations

import pt.lightweightform.lfkotlin.Context
import pt.lightweightform.lfkotlin.Issue
import pt.lightweightform.lfkotlin.SyncValidation
import pt.lightweightform.lfkotlin.objectOf

/**
 * Default issue code emitted by a `Unique` or `UniqueBy` validation when a collection has repeated
 * elements.
 */
public const val REPEATED_ELEMENTS_CODE: String = "LF_REPEATED_ELEMENTS"
/**
 * Issue typeof issues emitted by a `Unique` or `UniqueBy` validation when a collection has repeated
 * elements.
 */
public const val REPEATED_ELEMENTS_TYPE: String = "repeatedElements"

/**
 * Validation that ensures that an array doesn't contain duplicated elements.
 *
 * Depending on [emitAllRepetitions], an issue is emitted for each pair of repeated elements (when
 * `true`), or only for the first found repetition (when `false`). [emitAllRepetitions] defaults to
 * `true`.
 *
 * Each emitted issue has an `indices` data property with a list containing the two indices of the
 * conflicting elements. E.g. in JSON notation `{ indices: [0, 2] }` would indicate that elements
 * with index `0` and `2` are repeated.
 */
public open class Unique(
    private val repeatedElementsCode: String? = null,
    private val emitAllRepetitions: Boolean = true
) : SyncValidation> {
    override fun Context.validate(value: Array): Iterable = buildList {
        val map = mutableMapOf()
        for ((i, el) in value.withIndex()) {
            val conflict = map[el]
            if (conflict != null) {
                add(
                    Issue(
                        repeatedElementsCode ?: REPEATED_ELEMENTS_CODE,
                        data =
                            objectOf(
                                "type" to REPEATED_ELEMENTS_TYPE,
                                "indices" to listOf(conflict, i)
                            )
                    )
                )
                if (!emitAllRepetitions) {
                    break
                }
            } else {
                map[el] = i
            }
        }
    }
}

/**
 * Validation that ensures that an array doesn't contain duplicated elements, where the uniqueness
 * of an element is represented by its key as returned by [selector].
 *
 * Depending on [emitAllRepetitions], an issue is emitted for each pair of repeated elements (when
 * `true`), or only for the first found repetition (when `false`). [emitAllRepetitions] defaults to
 * `true`.
 *
 * Each emitted issue has an `indices` data property with a list containing the two indices of the
 * conflicting elements. E.g. in JSON notation `{ indices: [0, 2] }` would indicate that elements
 * with index `0` and `2` are repeated.
 *
 * `Pair`s, `Triple`s, and `List`s work well to represent composite keys. E.g. say that you have an
 * array of rows that should be unique in respect to their fields `A` and `B`; you can use
 * `UniqueBy` as such:
 *
 * ```kotlin
 * UniqueBy { Pair(it.A, it.B) }
 * ```
 *
 * When the result of calling [selector] on an element is `null`, that element is always considered
 * unique. I.e. if `selector(A) == null` and `selector(B) == null`, then `A` is considered different
 * to `B`.
 *
 * If you wish to treat multiple `null` values as equal to one another, consider wrapping said
 * values in an object (e.g. a list with a single element).
 */
public open class UniqueBy(
    private val repeatedElementsCode: String? = null,
    private val emitAllRepetitions: Boolean = true,
    private val selector: (T) -> K?,
) : SyncValidation> {
    override fun Context.validate(value: Array): Iterable = buildList {
        val map = mutableMapOf()
        for ((i, el) in value.withIndex()) {
            val key = selector(el)
            if (key != null) {
                val conflict = map[key]
                if (conflict != null) {
                    add(
                        Issue(
                            repeatedElementsCode ?: REPEATED_ELEMENTS_CODE,
                            data =
                                objectOf(
                                    "type" to REPEATED_ELEMENTS_TYPE,
                                    "indices" to listOf(conflict, i)
                                )
                        )
                    )
                    if (!emitAllRepetitions) {
                        break
                    }
                } else {
                    map[key] = i
                }
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy