
pimpathon.multimap.scala Maven / Gradle / Ivy
The newest version!
package pimpathon
import scala.collection.{breakOut, mutable ⇒ M, GenTraversable}
import scala.collection.generic.CanBuildFrom
import pimpathon.any._
import pimpathon.boolean._
import pimpathon.builder._
import pimpathon.function._
import pimpathon.map._
import pimpathon.stream._
import pimpathon.tuple._
object multiMap {
type MultiMap[F[_], K, V] = Map[K, F[V]]
type MMCBF[F[_], K, V] = CanBuildFrom[Nothing, (K, V), MultiMap[F, K, V]]
implicit def build[F[_], K, V](implicit fcbne: CanBuildNonEmpty[V, F[V]]): MMCBF[F, K, V] = MultiMap.build
implicit def multiMapPimps[F[_], K, V](multiMap: MultiMap[F, K, V]): MultiMapPimps[F, K, V] =
new MultiMapPimps[F, K, V](multiMap)
class MultiMapPimps[F[_], K, V](val value: MultiMap[F, K, V]) {
def select[W](f: F[V] ⇒ W): Map[K, W] = value.mapValuesEagerly(f) // just an alias for mapValuesEagerly
def merge(other: MultiMap[F, K, V])(implicit crf: CanRebuildFrom[F, V]): MultiMap[F, K, V] =
if (value.isEmpty) other else other.foldLeft(value) {
case (acc, (key, otherValues)) ⇒ acc.append(key, otherValues)
}
def append(key: K, newValues: F[V])(implicit crf: CanRebuildFrom[F, V]): MultiMap[F, K, V] =
value + ((key, crf.concat(List(value.get(key), Some(newValues)).flatten)))
def pop(key: K)(implicit crf: CanRebuildFrom[F, V]): MultiMap[F, K, V] = value.updateValue(key, crf.pop)
def onlyOption(implicit gtl: F[V] <:< GenTraversable[V], crf: CanRebuildFrom[F, V]): Option[Map[K, V]] =
headTailOption.flatMap(_.calcC(head ⇒ tail ⇒ tail.isEmpty.option(head)))
def sequence(implicit bf: CCBF[Map[K, V], F], gtl: F[V] <:< GenTraversable[V],
crf: CanRebuildFrom[F, V], crsm: CanRebuildFrom[F, Map[K, V]]
): F[Map[K, V]] = crsm.fromStream(value.unfold(_.headTailOption))
def headTailOption(implicit gtl: F[V] <:< GenTraversable[V], crf: CanRebuildFrom[F, V])
: Option[(Map[K, V], MultiMap[F, K, V])] = multiMap.head.filterSelf(_.nonEmpty).map(_ → multiMap.tail)
def flatMapValues[W](f: V ⇒ F[W])(implicit crfv: CanRebuildFrom[F, V], crfw: CanRebuildFrom[F, W])
: MultiMap[F, K, W] = value.mapValuesEagerly(crfv.flatMap(_)(f))
def getOrEmpty(k: K)(implicit fcbf: CCBF[V, F]): F[V] = value.getOrElse(k, fcbf.apply().result())
def multiMap: MultiMapConflictingPimps[F, K, V] = new MultiMapConflictingPimps[F, K, V](value)
}
class MultiMapConflictingPimps[F[_], K, V](value: MultiMap[F, K, V]) {
// These operations cannot be defined on MultiMapOps because non-implicit methods of the same name exist on Map
def head(implicit gtl: F[V] <:< GenTraversable[V]): Map[K, V] =
value.flatMap { case (k, fv) ⇒ fv.filterSelf(_.nonEmpty).map(k → _.head) }
def tail(implicit crf: CanRebuildFrom[F, V]): MultiMap[F, K, V] = value.updateValues(crf.pop _)
def values(implicit crf: CanRebuildFrom[F, V]): F[V] = crf.concat(value.values)
def reverse(implicit crf: CanRebuildFrom[F, V], cbf: CCBF[K, F]): MultiMap[F, V, K] =
value.toStream.flatMap(kvs ⇒ crf.toStream(kvs._2).map(_ → kvs._1))(collection.breakOut)
def mapEntries[C, W](f: K ⇒ F[V] ⇒ (C, F[W]))(
implicit cbmmf: MMCBF[F, C, F[W]], crf: CanRebuildFrom[F, W], crff: CanRebuildFrom[F, F[W]]
): MultiMap[F, C, W] = value.asMultiMap[F].withEntries(f.tupled).mapValuesEagerly(crf.concat)
def sliding(size: Int)(implicit bf: CCBF[Map[K, V], F], gtl: F[V] <:< GenTraversable[V],
crf: CanRebuildFrom[F, V], crsm: CanRebuildFrom[F, MultiMap[F, K, V]], fcbf: CanBuildFrom[Nothing, V, F[V]]
): F[MultiMap[F, K, V]] = {
crsm.fromStream(value.unfold(_.headTailOption).sliding(size)
.map(_.flatMap[(K, V), MultiMap[F, K, V]](_.toStream)(breakOut)).toStream)
}
}
object MultiMap {
def build[F[_], K, V](implicit fcbne: CanBuildNonEmpty[V, F[V]]): MMCBF[F, K, V] = new MultiMapCanBuildFrom[F, K, V]
def empty[F[_], K, V]: MultiMap[F, K, V] = Map.empty[K, F[V]]
}
trait IgnoreFromCBF[-From, -Elem, +To] extends CanBuildFrom[From, Elem, To] {
override def apply(from: From): M.Builder[Elem, To] = apply()
}
class MultiMapCanBuildFrom[F[_], K, V](implicit fcbne: CanBuildNonEmpty[V, F[V]])
extends MMCBF[F, K, V] with IgnoreFromCBF[Nothing, (K, V), MultiMap[F, K, V]] {
def apply(): M.Builder[(K, V), MultiMap[F, K, V]] = new MultiMapBuilder[F, K, V]
}
class MultiMapBuilder[F[_], K, V](
map: M.Map[K, M.Builder[V, F[V]]] = M.Map.empty[K, M.Builder[V, F[V]]]
)(
implicit fcbne: CanBuildNonEmpty[V, F[V]]
)
extends M.Builder[(K, V), MultiMap[F, K, V]] {
def +=(elem: (K, V)): this.type = { add(elem._1, elem._2); this }
def clear(): Unit = map.clear()
def result(): Map[K, F[V]] = map.map(kv ⇒ (kv._1, kv._2.result()))(breakOut)
private def add(k: K, v: V): Unit = map.put(k, map.get(k).map(_ += v).getOrElse(fcbne.builder(v)))
}
trait CanRebuildFrom[F[_], V] {
def concat(ffv: F[F[V]])(implicit crff: CanRebuildFrom[F, F[V]]): F[V] = concat(crff.toStream(ffv))
def concat(fvs: Iterable[F[V]]): F[V] = (cbf.apply() +++= fvs.map(toStream)) result()
def pop(fv: F[V]): Option[F[V]] = flatMapS(fv)(_.tailOption.filter(_.nonEmpty))
def flatMapS(fv: F[V])(f: Stream[V] ⇒ Option[Stream[V]]): Option[F[V]] = f(toStream(fv)).map(fromStream)
def toStream(fv: F[V]): Stream[V]
def flatMap[G[_], W](fv: F[V])(f: V ⇒ G[W])(implicit gcrf: CanRebuildFrom[G, W]): G[W] =
gcrf.fromStream(toStream(fv).flatMap(v ⇒ gcrf.toStream(f(v))))
protected val cbf: CanBuildFrom[F[V], V, F[V]]
def fromStream(to: TraversableOnce[V]): F[V] = (cbf() ++= to).result()
}
object CanRebuildFrom {
trait Unapply[FV] {
type F[_]
type V
def concat[G[_]](g_fv: G[FV])(implicit crf: CanRebuildFrom[G, FV]): F[V] =
fromStream(for { fv ← crf.toStream(g_fv); v ← toStream(fv) } yield v)
def fromStream(to: TraversableOnce[V]): F[V]
def toStream(fv: FV): Stream[V]
}
implicit def unapply[F0[_], V0](implicit crf: CanRebuildFrom[F0, V0])
: Unapply[F0[V0]] { type F[X] = F0[X]; type V = V0 } = new Unapply[F0[V0]] {
type F[X] = F0[X]
type V = V0
def fromStream(to: TraversableOnce[V]): F[V] = crf.fromStream(to)
def toStream(fv: F0[V0]): Stream[V] = crf.toStream(fv)
}
implicit def crf[F[_], V](
implicit cbf0: CanBuildFrom[F[V], V, F[V]], fTraversableOnce: F[V] <:< TraversableOnce[V]
): CanRebuildFrom[F, V] = new CanRebuildFrom[F, V] {
def toStream(fv: F[V]): Stream[V] = fTraversableOnce(fv).toStream
protected val cbf: CanBuildFrom[F[V], V, F[V]] = cbf0
}
}
}
trait CanBuildNonEmpty[-Elem, +To] {
def builder(head: Elem): M.Builder[Elem, To]
}
object CanBuildNonEmpty {
implicit def canBuildFromToCBNE[Elem, To](
implicit cbf: CanBuildFrom[Nothing, Elem, To]
): CanBuildNonEmpty[Elem, To] = new CanBuildNonEmpty[Elem, To] {
def builder(head: Elem): M.Builder[Elem, To] = cbf.apply() += head
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy