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

japgolly.microlibs.utils.StaticLookupFn.scala Maven / Gradle / Ivy

The newest version!
package japgolly.microlibs.utils

import japgolly.microlibs.stdlib_ext.MutableArray
import japgolly.univeq.UnivEq
import scala.annotation.nowarn
import scala.reflect.ClassTag

/** Fast, efficient lookup functions for static data.
  *
  * Creation verifies key uniqueness and throws runtime exceptions on failure.
  */
object StaticLookupFn {

  def useArray[A >: Null : ClassTag](as: Iterable[(Int, A)]): ArrayDsl[A] =
    if (as.isEmpty)
      new ArrayDsl[A] with Dsl.EmptyBase[Int, A] {
        override def to[V >: Null : ClassTag](ok: A => V, ko: Int => V) = ko
      }
    else
      new ArrayDsl[A] {
        override def total =
          to(identity, keyFail)

        override def toOption =
          to(Some(_), _ => None)

        override def toEither[E](e: Int => E) =
          to(Right(_), i => Left(e(i)))

        override def to[V >: Null : ClassTag](ok: A => V, ko: Int => V): Int => V = {
          val array = mkArray(ok)
          i => if (i >= 0 && i < array.length) {
            val a = array(i)
            if (null != a) a else ko(i)
          } else
            ko(i)
        }

        private def mkArray[X >: Null : ClassTag](toX: A => X): Array[X] = {
          val len = as.iterator.map(_._1).max + 1
          val aa = Array.fill[A](len)(null)
          val ax = Array.fill[X](len)(null)
          for ((i, a) <- as) {
            assert(i >= 0, s"Indices can't be negative. Found: $i")
            val a2 = aa(i)
            if (null != a2)
              fail(s"Duplicates for index $i: $a and $a2")
            aa(i) = a
            ax(i) = toX(a)
          }
          ax
        }

        override protected def iterator() = as.iterator
      }

  def useArrayBy[A >: Null : ClassTag](as: Iterable[A])(key: A => Int): DslBase[Int, A] =
    useArray(as.map(a => (key(a), a)))

  trait ArrayDsl[A] extends DslBase[Int, A] {
    def to[V >: Null : ClassTag](ok: A => V, ko: Int => V): Int => V
  }

  // ===================================================================================================================

  @nowarn("cat=unused")
  def useMap[K: UnivEq, V](kvs: Iterable[(K, V)]): Dsl[K, V] =
    if (kvs.isEmpty)
      Dsl.empty
    else
      new Dsl[K, V] {
        override def toOption: K => Option[V] =
          if (kvs.size <= 4)
            super.toOption
          else
            mkMap(identity).get

        override def to[A](ok: V => A, ko: K => A): K => A = {
          val m = mkMap(ok)
          k => m.getOrElse(k, ko(k))
        }

        private def mkMap[X](toX: V => X): Map[K, X] = {
          var mv = Map.empty[K, V]
          var mx = Map.empty[K, X]
          for ((k, v) <- kvs)
            mv.get(k) match {
              case None     => mv = mv.updated(k, v); mx = mx.updated(k, toX(v))
              case Some(v2) => fail(s"Duplicates for $k: ${v2} and $v")
            }
          mx
        }

        override protected def iterator() = kvs.iterator
      }

  def useMapBy[K: UnivEq, V](vs: Iterable[V])(k: V => K) =
    useMap(vs.map(v => k(v) -> v))


  // ===================================================================================================================

  trait DslBase[@specialized(Int) K, V] {
    def total: K => V
    def toOption: K => Option[V]
    def toEither[E](e: K => E): K => Either[E, V]

    def toEitherWithHelp[H, E](h: V => String, sep: String = ",")(e: (K, String) => E): K => Either[E, V] = {
      val help = MutableArray(iterator().map(_._2).map(h)).sort.mkString(sep)
      toEither(e(_, help))
    }

    protected def iterator(): Iterator[(K, V)]
  }

  trait Dsl[@specialized(Int) K, V] extends DslBase[K, V] {
    override def total: K => V =
      to(identity, keyFail)

    override def toOption: K => Option[V] =
      to(Some(_), _ => None)

    override def toEither[E](e: K => E): K => Either[E, V] =
      to(Right(_), k => Left(e(k)))

    def to[A](ok: V => A, ko: K => A): K => A
  }

  object Dsl {
    def empty[@specialized(Int) K, V]: Dsl[K, V] =
      new Dsl[K, V] with EmptyBase[K, V] {
        override def to[A](ok: V => A, ko: K => A) = ko
      }

    trait EmptyBase[@specialized(Int) K, V] extends DslBase[K, V] {
      override def total = keyFail
      override def toOption = _ => None
      override def toEither[E](e: K => E) = k => Left(e(k))
      override protected def iterator() = Iterator.empty
    }
  }

  private def assert(t: Boolean, e: => String): Unit =
    if (!t) fail(e)

  private def fail(e: String): Nothing =
    throw new ExceptionInInitializerError(e)

  private val keyFail: Any => Nothing =
    k => throw new NoSuchElementException("key not found: " + k)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy