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

commonMain.dev.inmo.micro_utils.common.MapDiff.kt Maven / Gradle / Ivy

package dev.inmo.micro_utils.common

/**
 * Contains diff based on the comparison of objects with the same [K].
 *
 * @param removed Contains map with keys removed from parent map
 * @param changed Contains map with keys values changed new map in comparison with old one
 * @param added Contains map with new keys and values
 */
data class MapDiff @Warning(warning) constructor(
    val removed: Map,
    val changed: Map>,
    val added: Map
) {
    fun isEmpty() = removed.isEmpty() && changed.isEmpty() && added.isEmpty()
    inline fun isNotEmpty() = !isEmpty()

    companion object {
        private const val warning = "This feature can be changed without any warranties. Use with caution and only in case you know what you are doing"
        fun  empty() = MapDiff(emptyMap(), emptyMap(), emptyMap())
    }
}

private inline fun  createCompareFun(
    strictComparison: Boolean
): (K, V, V) -> Boolean = if (strictComparison) {
    { _, first, second -> first === second }
} else {
    { _, first, second -> first == second }
}

/**
 * Compare [this] [Map] with the [other] one in principle when [other] is newer than [this]
 *
 * @param compareFun Will be used to determine changed values
 */
fun  Map.diff(
    other: Map,
    compareFun: (K, V, V) -> Boolean
): MapDiff {
    val removed: Map = (keys - other.keys).associateWith {
        getValue(it)
    }
    val added: Map = (other.keys - keys).associateWith {
        other.getValue(it)
    }
    val changed = keys.intersect(other.keys).mapNotNull {
        val old = getValue(it)
        val new = other.getValue(it)
        if (compareFun(it, old, new)) {
            return@mapNotNull null
        } else {
            it to (old to new)
        }
    }.toMap()

    return MapDiff(
        removed,
        changed,
        added
    )
}

/**
 * Compare [this] [Map] with the [other] one in principle when [other] is newer than [this]
 *
 * @param strictComparison If true, will use strict (===) comparison for the values' comparison. Otherwise, standard
 * `equals` will be used
 */
fun  Map.diff(
    other: Map,
    strictComparison: Boolean = false
): MapDiff = diff(
    other,
    compareFun = createCompareFun(strictComparison)
)

/**
 * Will apply [mapDiff] to [this] [MutableMap]
 */
fun  MutableMap.applyDiff(
    mapDiff: MapDiff
) {
    mapDiff.apply {
        removed.keys.forEach { remove(it) }
        changed.forEach { (k, oldNew) ->
            put(k, oldNew.second)
        }
        added.forEach { (k, new) ->
            put(k, new)
        }
    }
}

/**
 * Will apply changes with [from] map into [this] one
 *
 * @param compareFun Will be used to determine changed values
 *
 * @return [MapDiff] applied to [this] [MutableMap]
 */
fun  MutableMap.applyDiff(
    from: Map,
    compareFun: (K, V, V) -> Boolean
): MapDiff {
    return diff(from, compareFun).also {
        applyDiff(it)
    }
}

/**
 * Will apply changes with [from] map into [this] one
 *
 * @param strictComparison If true, will use strict (===) comparison for the values' comparison. Otherwise, standard
 * `equals` will be used
 *
 * @return [MapDiff] applied to [this] [MutableMap]
 */
fun  MutableMap.applyDiff(
    from: Map,
    strictComparison: Boolean = false
): MapDiff = applyDiff(
    from,
    compareFun = createCompareFun(strictComparison)
)

/**
 * Reverse [this] [MapDiff]. Result will contain [MapDiff.added] on [MapDiff.removed] (and vice-verse), all the
 * [MapDiff.changed] values will be reversed too
 */
fun  MapDiff.reversed(): MapDiff = MapDiff(
    removed = added,
    changed = changed.mapValues { (_, oldNew) -> oldNew.second to oldNew.first },
    added = removed
)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy