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

pimpathon.map.scala Maven / Gradle / Ivy

package pimpathon

import scala.collection.{breakOut, mutable ⇒ M, GenTraversable, GenTraversableLike, GenTraversableOnce}
import scala.collection.immutable.{SortedMap, TreeMap}

import pimpathon.any._
import pimpathon.function._
import pimpathon.multiMap._
import pimpathon.tuple._


object map extends genTraversableLike[GenTraversable] {
  implicit def mapPimps[K, V](map: Map[K, V]): MapPimps[K, V] = new MapPimps[K, V](map)

  class MapPimps[K, V](map: Map[K, V]) {
    def containsAny(ok: Option[K]): Boolean = ok.exists(map.contains)
    def containsAll(ok: Option[K]): Boolean = ok.forall(map.contains)
    def containsAny[GK <: GenTraversableOnce[K]](gk: GK): Boolean = gk.exists(map.contains)
    def containsAll[GK <: GenTraversableOnce[K]](gk: GK): Boolean = gk.forall(map.contains)
    def containsEntry(kv: (K, V)): Boolean = containsEntry(kv._1, kv._2)
    def containsEntry(k: K, v: V): Boolean = map.get(k) == Some(v)

    def get(ok: Option[K]): Option[V]               = ok.flatMap(map.get)
    def getOrThrow(k: K, message: String): V        = getOrThrow(k, new IllegalArgumentException(message))
    def getOrThrow(k: K, exception: ⇒ Exception): V = map.getOrElse(k, throw exception)

    def findKey(p: Predicate[K]): Option[K]   = keyFor.matchingKey(p)
    def findValue(p: Predicate[V]): Option[V] = valueFor.matchingValue(p)

    def filterKeysNot(p: Predicate[K]): Map[K, V]   = map.filterNot(kv ⇒ p(kv._1))
    def filterValuesNot(p: Predicate[V]): Map[K, V] = map.filterNot(kv ⇒ p(kv._2))
    def filterValues(p: Predicate[V]): Map[K, V]    = map.filter(kv ⇒ p(kv._2))


    def keyExists(p: Predicate[K]): Boolean   = map.exists(kv ⇒ p(kv._1))
    def valueExists(p: Predicate[V]): Boolean = map.exists(kv ⇒ p(kv._2))

    def emptyTo(empty: ⇒ Map[K, V]): Map[K, V]            = uncons(empty, _ ⇒ map)
    def calcIfNonEmpty[A](f: Map[K, V] ⇒ A): Option[A]    = map.calcIf(_.nonEmpty)(f)
    def uncons[A](empty: ⇒ A, nonEmpty: Map[K, V] ⇒ A): A = if (map.isEmpty) empty else nonEmpty(map)

    def reverse(f: Set[K] ⇒ K): Map[V, K] = reverseToMultiMap.mapValuesEagerly(f)
    def reverseToMultiMap: MultiMap[Set, V, K] = map.map(_.swap)(breakOut)

    def sorted(implicit ordering: Ordering[K]): SortedMap[K, V] = TreeMap.empty[K, V](ordering) ++ map

    def composeM[C](other: Map[C, K]): Map[C, V] = other.andThenM(map)
    def andThenM[W](other: Map[V, W]): Map[K, W] = updateValues(other.get _)

    def toMutable: M.Map[K, V] = mutable
    def mutable: M.Map[K, V] = M.Map.empty[K, V] ++ map

    def entryFor: MapAndThen[K, V, (K, V)] = new MapAndThen[K, V, (K, V)](map, identity[(K, V)])
    def keyFor:   MapAndThen[K, V, K]      = new MapAndThen[K, V, K](map, key)
    def valueFor: MapAndThen[K, V, V]      = new MapAndThen[K, V, V](map, value)

    def partitionKeysBy[C](pf: PartialFunction[K, C]): (Map[K, V], Map[C, V])   = partitionEntriesBy(pf.first[V])
    def partitionValuesBy[W](pf: PartialFunction[V, W]): (Map[K, V], Map[K, W]) = partitionEntriesBy(pf.second[K])

    def partitionEntriesBy[C, W](pf: PartialFunction[(K, V), (C, W)]): (Map[K, V], Map[C, W]) =
      map.partition(pf.isUndefinedAt).tmap(identity, _.map(pf))

    def mapKeysEagerly[C](f: K ⇒ C): Map[C, V]         = map.map { case (k, v) ⇒ (f(k), v) }(breakOut)
    def mapValuesEagerly[W](f: V ⇒ W): Map[K, W]       = map.map { case (k, v) ⇒ (k, f(v)) }(breakOut)
    def mapEntries[C, W](f: K ⇒ V ⇒ (C, W)): Map[C, W] = map.map { case (k, v) ⇒ f(k)(v)   }(breakOut)

    def collectKeys[C](pf: PartialFunction[K, C]): Map[C, V] = map.collect(pf.first)
    def collectValues[W](pf: PartialFunction[V, W]): Map[K, W] = map.collect(pf.second)

    def updateValue(key: K, f: V ⇒ Option[V]): Map[K, V] =
      map.get(key).flatMap(f).map(newValue ⇒ map + ((key, newValue))).getOrElse(map - key)

    def updateKeys[C](pf: PartialFunction[K, C]): Map[C, V] = updateKeys(pf.lift)
    def updateValues[W](pf: PartialFunction[V, W]): Map[K, W] = updateValues(pf.lift)

    def updateKeys[C](f: K ⇒ Option[C]): Map[C, V]   = map.flatMap(kv ⇒ f(kv._1).map(_ → kv._2))
    def updateValues[W](f: V ⇒ Option[W]): Map[K, W] = map.flatMap(kv ⇒ f(kv._2).map(kv._1 → _))
  }

  class MapAndThen[K, V, A](map: Map[K, V], andThen: ((K, V)) ⇒ A) {
    def maxValue(implicit O: Ordering[V]): Option[A] = map.calcIfNonEmpty(_.maxBy(value)).map(andThen)
    def minValue(implicit O: Ordering[V]): Option[A] = map.calcIfNonEmpty(_.minBy(value)).map(andThen)
    def maxKey(implicit O: Ordering[K]): Option[A] = map.calcIfNonEmpty(_.maxBy(key)).map(andThen)
    def minKey(implicit O: Ordering[K]): Option[A] = map.calcIfNonEmpty(_.minBy(key)).map(andThen)

    def matchingKey(p: Predicate[K]): Option[A]   = map.find(kv ⇒ p(kv._1)).map(andThen)
    def matchingValue(p: Predicate[V]): Option[A] = map.find(kv ⇒ p(kv._2)).map(andThen)
  }

  protected def toGTL[A](gt: GenTraversable[A]): GenTraversableLike[A, GenTraversable[A]] = gt

  @inline private def key[K, V]:   ((K, V)) ⇒ K = (kv: (K, V)) ⇒ kv._1
  @inline private def value[K, V]: ((K, V)) ⇒ V = (kv: (K, V)) ⇒ kv._2
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy