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

commonMain.arrow.core.map.kt Maven / Gradle / Ivy

There is a newer version: 2.0.1
Show newest version
@file:OptIn(ExperimentalTypeInference::class)

package arrow.core

import arrow.core.raise.either
import arrow.core.raise.mapOrAccumulate
import arrow.core.raise.RaiseAccumulate
import arrow.core.raise.mapValuesOrAccumulate
import kotlin.experimental.ExperimentalTypeInference

/**
 * Combines to structures by taking the intersection of their shapes
 * and using `Pair` to hold the elements.
 *
 * ```kotlin
 * import arrow.core.zip
 * import io.kotest.matchers.shouldBe
 *
 * fun test() {
 *   mapOf(1 to "A", 2 to "B")
 *     .zip(mapOf(1 to "1", 2 to "2", 3 to "3")) shouldBe mapOf(1 to Pair("A", "1"), 2 to Pair("B", "2"))
 * }
 * ```
 * 
 * 
 */
public fun  Map.zip(other: Map): Map> =
  zip(other) { _, a, b -> Pair(a, b) }

/**
 * Combines to structures by taking the intersection of their shapes
 * and combining the elements with the given function.
 *
 * ```kotlin
 * import arrow.core.zip
 * import io.kotest.matchers.shouldBe
 *
 * fun test() {
 *   mapOf(1 to "A", 2 to "B").zip(mapOf(1 to "1", 2 to "2", 3 to "3")) {
 *     _, a, b -> "$a ~ $b"
 *   } shouldBe mapOf(1 to "A ~ 1", 2 to "B ~ 2")
 * }
 * ```
 * 
 * 
 */
@Suppress("UNCHECKED_CAST")
public inline fun  Map.zip(other: Map, map: (Key, A, B) -> C): Map =
  buildMap(size) {
    [email protected] { (key, bb) ->
      if (other.containsKey(key)) {
        put(key, map(key, bb, other[key] as B))
      }
    }
  }

@Suppress("UNCHECKED_CAST")
public inline fun  Map.zip(
  c: Map,
  d: Map,
  map: (Key, B, C, D) -> E
): Map = buildMap(size) {
  [email protected] { (key, bb) ->
    if (c.containsKey(key) && d.containsKey(key)) {
      val cc = c[key] as C
      val dd = d[key] as D

      put(key, map(key, bb, cc, dd))
    }
  }
}

@Suppress("UNCHECKED_CAST")
public inline fun  Map.zip(
  c: Map,
  d: Map,
  e: Map,
  map: (Key, B, C, D, E) -> F
): Map = buildMap(size) {
  [email protected] { (key, bb) ->
    if (c.containsKey(key) && d.containsKey(key) && e.containsKey(key)) {
      val cc = c[key] as C
      val dd = d[key] as D
      val ee = e[key] as E

      put(key, map(key, bb, cc, dd, ee))
    }
  }
}

@Suppress("UNCHECKED_CAST")
public inline fun  Map.zip(
  c: Map,
  d: Map,
  e: Map,
  f: Map,
  map: (Key, B, C, D, E, F) -> G
): Map = buildMap(size) {
  [email protected] { (key, bb) ->
    if (c.containsKey(key) && d.containsKey(key) && e.containsKey(key) && f.containsKey(key)) {
      val cc = c[key] as C
      val dd = d[key] as D
      val ee = e[key] as E
      val ff = f[key] as F

      put(key, map(key, bb, cc, dd, ee, ff))
    }
  }
}

@Suppress("UNCHECKED_CAST")
public inline fun  Map.zip(
  c: Map,
  d: Map,
  e: Map,
  f: Map,
  g: Map,
  map: (Key, B, C, D, E, F, G) -> H
): Map = buildMap(size) {
  [email protected] { (key, bb) ->
    if (c.containsKey(key) && d.containsKey(key) && e.containsKey(key) && f.containsKey(key) && g.containsKey(key)) {
      val cc = c[key] as C
      val dd = d[key] as D
      val ee = e[key] as E
      val ff = f[key] as F
      val gg = g[key] as G

      put(key, map(key, bb, cc, dd, ee, ff, gg))
    }
  }
}

@Suppress("UNCHECKED_CAST")
public inline fun  Map.zip(
  c: Map,
  d: Map,
  e: Map,
  f: Map,
  g: Map,
  h: Map,
  map: (Key, B, C, D, E, F, G, H) -> I
): Map = buildMap(size) {
  [email protected] { (key, bb) ->
    if (c.containsKey(key) && d.containsKey(key) && e.containsKey(key) && f.containsKey(key) && g.containsKey(key) && h.containsKey(key)) {
      val cc = c[key] as C
      val dd = d[key] as D
      val ee = e[key] as E
      val ff = f[key] as F
      val gg = g[key] as G
      val hh = h[key] as H

      put(key, map(key, bb, cc, dd, ee, ff, gg, hh))
    }
  }
}

@Suppress("UNCHECKED_CAST")
public inline fun  Map.zip(
  c: Map,
  d: Map,
  e: Map,
  f: Map,
  g: Map,
  h: Map,
  i: Map,
  map: (Key, B, C, D, E, F, G, H, I) -> J
): Map = buildMap(size) {
  [email protected] { (key, bb) ->
    if (c.containsKey(key) && d.containsKey(key) && e.containsKey(key) && f.containsKey(key) && g.containsKey(key) && h.containsKey(key) && i.containsKey(key)) {
      val cc = c[key] as C
      val dd = d[key] as D
      val ee = e[key] as E
      val ff = f[key] as F
      val gg = g[key] as G
      val hh = h[key] as H
      val ii = i[key] as I

      put(
        key, map(key, bb, cc, dd, ee, ff, gg, hh, ii)
      )
    }
  }
}

@Suppress("UNCHECKED_CAST")
public inline fun  Map.zip(
  c: Map,
  d: Map,
  e: Map,
  f: Map,
  g: Map,
  h: Map,
  i: Map,
  j: Map,
  map: (Key, B, C, D, E, F, G, H, I, J) -> K
): Map = buildMap(size) {
  [email protected] { (key, bb) ->
    if (c.containsKey(key) && d.containsKey(key) && e.containsKey(key) && f.containsKey(key) && g.containsKey(key) && h.containsKey(key) && i.containsKey(key) && j.containsKey(key)) {
      val cc = c[key] as C
      val dd = d[key] as D
      val ee = e[key] as E
      val ff = f[key] as F
      val gg = g[key] as G
      val hh = h[key] as H
      val ii = i[key] as I
      val jj = j[key] as J

      put(key, map(key, bb, cc, dd, ee, ff, gg, hh, ii, jj))
    }
  }
}

@Suppress("UNCHECKED_CAST")
public inline fun  Map.zip(
  c: Map,
  d: Map,
  e: Map,
  f: Map,
  g: Map,
  h: Map,
  i: Map,
  j: Map,
  k: Map,
  map: (Key, B, C, D, E, F, G, H, I, J, K) -> L
): Map = buildMap(size) {
  [email protected] { (key, bb) ->
    if (c.containsKey(key) && d.containsKey(key) && e.containsKey(key) && f.containsKey(key) && g.containsKey(key) && h.containsKey(key) && i.containsKey(key) && j.containsKey(key) && k.containsKey(key)) {
      val cc = c[key] as C
      val dd = d[key] as D
      val ee = e[key] as E
      val ff = f[key] as F
      val gg = g[key] as G
      val hh = h[key] as H
      val ii = i[key] as I
      val jj = j[key] as J
      val kk = k[key] as K

      put(key, map(key, bb, cc, dd, ee, ff, gg, hh, ii, jj, kk))
    }
  }
}

/**
 * Transform every [Map.Entry] of the original [Map] using [f],
 * only keeping the [Map.Entry] of the transformed map that match the input [Map.Entry].
 */
@Suppress("UNCHECKED_CAST")
public fun  Map.flatMapValues(f: (Map.Entry) -> Map): Map =
  buildMap {
    [email protected] { entry ->
      val nestedMap = f(entry)
      if (nestedMap.containsKey(entry.key)) {
        put(entry.key, nestedMap[entry.key] as B)
      }
    }
  }

@Deprecated(
  message = "Deprecated to allow for future alignment with stdlib Map#map returning List",
  replaceWith = ReplaceWith("mapValuesOrAccumulate(combine, transform)"),
)
public inline fun  Map.mapOrAccumulate(
  combine: (E, E) -> E,
  @BuilderInference transform: RaiseAccumulate.(Map.Entry) -> B
): Either> = mapValuesOrAccumulate(combine, transform)

@Deprecated(
  message = "Deprecated to allow for future alignment with stdlib Map#map returning List",
  replaceWith = ReplaceWith("mapValuesOrAccumulate(transform)"),
)
public inline fun  Map.mapOrAccumulate(
  @BuilderInference transform: RaiseAccumulate.(Map.Entry) -> B
): Either, Map> = mapValuesOrAccumulate(transform)

public inline fun  Map.mapValuesOrAccumulate(
  combine: (E, E) -> E,
  @BuilderInference transform: RaiseAccumulate.(Map.Entry) -> B
): Either> = either {
  mapValuesOrAccumulate(this@mapValuesOrAccumulate, combine, transform)
}

public inline fun  Map.mapValuesOrAccumulate(
  @BuilderInference transform: RaiseAccumulate.(Map.Entry) -> B
): Either, Map> = either {
  mapValuesOrAccumulate(this@mapValuesOrAccumulate, transform)
}

public inline fun  Map.mapValuesNotNull(transform: (Map.Entry) -> B?): Map =
  buildMap {
    [email protected] { entry ->
      transform(entry)?.let { put(entry.key, it) }
    }
  }

public fun  Map>.filterOption(): Map =
  buildMap {
    [email protected] { (key, option) ->
      option.fold({ }, { put(key, it) })
    }
  }

/**
 * Returns a Map containing all elements that are instances of specified type parameter R.
 */
@Suppress("UNCHECKED_CAST")
public inline fun  Map.filterIsInstance(): Map =
  filterValues { it is R } as Map

/**
 * Combines two structures by taking the union of their shapes and using Ior to hold the elements.
 *
 * ```kotlin
 * import arrow.core.*
 * import io.kotest.matchers.shouldBe
 *
 * fun test() {
 *   val res = mapOf(1 to 1, 2 to 2).align(mapOf(1 to "1", 2 to "2", 3 to "3"))
 *   res shouldBe mapOf(1 to Ior.Both(1, "1"), 2 to Ior.Both(2, "2"), 3 to Ior.Right("3"))
 * }
 * ```
 * 
 * 
 */
public fun  Map.align(b: Map): Map> =
  padZip(b, { _, a -> Ior.Left(a) }, { _, bb -> Ior.Right(bb) }) { _, a, bb -> Ior.Both(a, bb) }

/**
 * Combines two structures by taking the union of their shapes and combining the elements with the given function.
 *
 * ```kotlin
 * import arrow.core.*
 * import io.kotest.matchers.shouldBe
 *
 * fun test() {
 *   mapOf("1" to 1, "2" to 2)
 *     .align(mapOf("1" to 1, "2" to 2, "3" to 3)) { (_, a) ->
 *       "$a"
 *     } shouldBe mapOf("1" to "Ior.Both(1, 1)", "2" to Ior.Both(2, 2), "3" to Ior.Right(3))
 * }
 * ```
 * 
 * 
 */
public fun  Map.align(b: Map, fa: (Map.Entry>) -> C): Map =
  padZip(
    b,
    { k, a -> fa(Entry(k, Ior.Left(a))) },
    { k, bb -> fa(Entry(k, Ior.Right(bb))) }
  ) { k, a, bb -> fa(Entry(k, Ior.Both(a, bb))) }

private class Entry(override val key: K, override val value: V) : Map.Entry {
  override fun hashCode(): Int = key.hashCode() xor value.hashCode()
  override fun toString(): String = "$key=$value"
  override fun equals(other: Any?): Boolean =
    other is Map.Entry<*, *> && other.key == key && other.value == value
}

public fun  Map.salign(other: Map, combine: (A, A) -> A): Map =
  padZip(other, { _, a -> a }, { _, b -> b }) { _, a, b -> combine(a, b) }

/**
 * Align two structures as in zip, but filling in blanks with null.
 */
public fun  Map.padZip(other: Map): Map> =
  padZip(other) { _, a, b -> a to b }

public fun  Map.padZip(other: Map, fa: (K, A?, B?) -> C): Map =
  padZip(other, { k, a -> fa(k, a, null) }, { k, b -> fa(k, null, b) }) { k, a, b -> fa(k, a, b) }

@Suppress("UNCHECKED_CAST")
public inline fun  Map.padZip(
  other: Map,
  left: (K, A) -> C,
  right: (K, B) -> C,
  both: (K, A, B) -> C
): Map = buildMap {
  ([email protected] + other.keys).forEach { key ->
    when {
      [email protected](key) && other.containsKey(key) ->
        put(key, both(key, this@padZip[key] as A, other[key] as B))

      [email protected](key) -> put(key, left(key, this@padZip[key] as A))
      other.containsKey(key) -> put(key, right(key, other[key] as B))
    }
  }
}

/**
 * Splits a union into its component parts.
 *
 * ```kotlin
 * import arrow.core.*
 * import io.kotest.matchers.shouldBe
 *
 * fun test() {
 *   mapOf(
 *     "first" to Ior.Both("A", 1),
 *     "second" to Ior.Both("B", 2),
 *     "third" to Ior.Left("C")
 *   ).unalign() shouldBe Pair(mapOf("first" to "A", "second" to "B", "third" to "C"), mapOf("first" to 1, "second" to 2))
 * }
 * ```
 * 
 * 
 */
public fun  Map>.unalign(): Pair, Map> =
  unalign { (_, ior) -> ior }

/**
 * after applying the given function, splits the resulting union shaped structure into its components parts
 *
 * ```kotlin
 * import arrow.core.*
 * import io.kotest.matchers.shouldBe
 *
 * fun test() {
 *   mapOf("1" to 1, "2" to 2, "3" to 3)
 *     .unalign { (key, value) ->
 *       when(key) {
 *         "1" -> Ior.Left(value)
 *         "2" -> Ior.Right(key)
 *         else -> Ior.Both(value, key)
 *       }
 *     } shouldBe Pair(mapOf("1" to 1, "3" to 3), mapOf("2" to 2, "3" to 3))
 * }
 * ```
 * 
 * 
 */
public inline fun  Map.unalign(fa: (Map.Entry) -> Ior): Pair, Map> {
  val lefts = mutableMapOf()
  val rights = mutableMapOf()
  forEach { entry ->
    fa(entry).fold(
      { lefts[entry.key] = it },
      { rights[entry.key] = it },
      { a, b ->
        lefts[entry.key] = a
        rights[entry.key] = b
      }
    )
  }
  return lefts to rights
}

/**
 * Unzips the structure holding the resulting elements in an `Pair`
 *
 * ```kotlin
 * import arrow.core.*
 * import io.kotest.matchers.shouldBe
 *
 * fun test() {
 *   mapOf(
 *     "first" to ("A" to 1),
 *     "second" to ("B" to 2)
 *   ).unzip() shouldBe Pair(mapOf("first" to "A", "second" to "B"), mapOf("first" to 1, "second" to 2))
 * }
 * ```
 * 
 * 
 */
public fun  Map>.unzip(): Pair, Map> =
  unzip { (_, pair) -> pair }

/**
 * After applying the given function unzip the resulting structure into its elements.
 *
 * ```kotlin
 * import arrow.core.*
 * import io.kotest.matchers.shouldBe
 *
 * fun test() {
 *   mapOf("first" to "A:1", "second" to "B:2", "third" to "C:3").unzip { (_, e) ->
 *     e.split(":").let {
 *       it.first() to it.last()
 *     }
 *   } shouldBe Pair(
 *     mapOf("first" to "A", "second" to "B", "third" to "C"),
 *     mapOf("first" to "1", "second" to "2", "third" to "3")
 *   )
 * }
 * ```
 * 
 * 
 */
public inline fun  Map.unzip(fc: (Map.Entry) -> Pair): Pair, Map> {
  val lefts = mutableMapOf()
  val rights = mutableMapOf()
  forEach { entry ->
    val (a, b) = fc(entry)
    lefts[entry.key] = a
    rights[entry.key] = b
  }
  return lefts to rights
}

@Suppress("UNCHECKED_CAST")
public fun  Map.getOrNone(key: K): Option =
  if (containsKey(key)) Some(get(key) as V) else None

/** Combines two maps using [combine] to combine values for the same key. */
public fun  Map.combine(other: Map, combine: (A, A) -> A): Map =
  if (size < other.size) fold(other) { my, (k, b) -> my + Pair(k, my[k]?.let { combine(b, it) } ?: b) }
  else other.fold(this@combine) { my, (k, a) -> my + Pair(k, my[k]?.let { combine(a, it) } ?: a) }

public inline fun  Map.fold(initial: B, operation: (acc: B, Map.Entry) -> B): B {
  var accumulator = initial
  forEach { accumulator = operation(accumulator, it) }
  return accumulator
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy