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

scala.collection.compat.PackageShared.scala Maven / Gradle / Ivy

The newest version!
/*
 * Scala (https://www.scala-lang.org)
 *
 * Copyright EPFL and Lightbend, Inc.
 *
 * Licensed under Apache License 2.0
 * (http://www.apache.org/licenses/LICENSE-2.0).
 *
 * See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 */

package scala.collection.compat

import scala.annotation.{nowarn, tailrec}
import scala.collection.generic._
import scala.reflect.ClassTag
import scala.collection.{
  GenTraversable,
  IterableLike,
  IterableView,
  MapLike,
  TraversableLike,
  immutable => i,
  mutable => m
}
import scala.runtime.{Tuple2Zipped, Tuple3Zipped}
import scala.util.Random
import scala.{collection => c}

/** The collection compatibility API */
private[compat] trait PackageShared {
  import CompatImpl._

  /**
   * A factory that builds a collection of type `C` with elements of type `A`.
   *
   * @tparam A Type of elements (e.g. `Int`, `Boolean`, etc.)
   * @tparam C Type of collection (e.g. `List[Int]`, `TreeMap[Int, String]`, etc.)
   */
  type Factory[-A, +C] = CanBuildFrom[Nothing, A, C]

  implicit class FactoryOps[-A, +C](private val factory: Factory[A, C]) {

    /**
     * @return A collection of type `C` containing the same elements
     *         as the source collection `it`.
     * @param it Source collection
     */
    def fromSpecific(it: TraversableOnce[A]): C = (factory() ++= it).result()

    /** Get a Builder for the collection. For non-strict collection types this will use an intermediate buffer.
     * Building collections with `fromSpecific` is preferred because it can be lazy for lazy collections. */
    def newBuilder: m.Builder[A, C] = factory()
  }

  implicit class StringOps(s: String) {

    /**
     * Try to parse as a `Boolean`
     * @return `Some(true)` if the string is "true" case insensitive,
     * `Some(false)` if the string is "false" case insensitive,
     * and `None` if the string is anything else
     * @throws java.lang.NullPointerException if the string is `null`
     */
    def toBooleanOption: Option[Boolean] = StringParsers.parseBool(s)

    /**
     * Try to parse as a `Byte`
     * @return `Some(value)` if the string contains a valid byte value, otherwise `None`
     * @throws java.lang.NullPointerException if the string is `null`
     */
    def toByteOption: Option[Byte] = StringParsers.parseByte(s)

    /**
     * Try to parse as a `Short`
     * @return `Some(value)` if the string contains a valid short value, otherwise `None`
     * @throws java.lang.NullPointerException if the string is `null`
     */
    def toShortOption: Option[Short] = StringParsers.parseShort(s)

    /**
     * Try to parse as an `Int`
     * @return `Some(value)` if the string contains a valid Int value, otherwise `None`
     * @throws java.lang.NullPointerException if the string is `null`
     */
    def toIntOption: Option[Int] = StringParsers.parseInt(s)

    /**
     * Try to parse as a `Long`
     * @return `Some(value)` if the string contains a valid long value, otherwise `None`
     * @throws java.lang.NullPointerException if the string is `null`
     */
    def toLongOption: Option[Long] = StringParsers.parseLong(s)

    /**
     * Try to parse as a `Float`
     * @return `Some(value)` if the string is a parsable `Float`, `None` otherwise
     * @throws java.lang.NullPointerException If the string is null
     */
    def toFloatOption: Option[Float] = StringParsers.parseFloat(s)

    /**
     * Try to parse as a `Double`
     * @return `Some(value)` if the string is a parsable `Double`, `None` otherwise
     * @throws java.lang.NullPointerException If the string is null
     */
    def toDoubleOption: Option[Double] = StringParsers.parseDouble(s)
  }

  implicit def genericCompanionToCBF[A, CC[X] <: GenTraversable[X]](
      fact: GenericCompanion[CC]): CanBuildFrom[Any, A, CC[A]] = {
    /* see https://github.com/scala/scala-collection-compat/issues/337
       `simpleCBF.apply` takes a by-name parameter and relies on
       repeated references generating new builders, thus this expression
       must be non-strict
     */
    def builder: m.Builder[A, CC[A]] = fact match {
      case c.Seq | i.Seq =>
        new IdentityPreservingBuilder[A, i.Seq](i.Seq.newBuilder[A])

      case c.LinearSeq | i.LinearSeq =>
        new IdentityPreservingBuilder[A, i.LinearSeq](i.LinearSeq.newBuilder[A])
      case i.Queue =>
        new IdentityPreservingBuilder[A, i.Queue](i.Queue.newBuilder[A])
      case i.Stream =>
        new IdentityPreservingBuilder[A, i.Stream](i.Stream.newBuilder[A])
      case i.Stack =>
        new IdentityPreservingBuilder[A, i.Stack](i.Stack.newBuilder[A]): @nowarn("cat=deprecation")
      case i.List =>
        new IdentityPreservingBuilder[A, i.List](i.List.newBuilder[A])

      case c.IndexedSeq | i.IndexedSeq =>
        new IdentityPreservingBuilder[A, i.IndexedSeq](i.IndexedSeq.newBuilder[A])
      case i.Vector =>
        new IdentityPreservingBuilder[A, i.Vector](i.Vector.newBuilder[A])

      case c.Set | i.Set =>
        new IdentityPreservingBuilder[A, i.Set](i.Set.newBuilder[A])

      case i.HashSet =>
        new IdentityPreservingBuilder[A, i.HashSet](i.HashSet.newBuilder[A])
      case i.ListSet =>
        new IdentityPreservingBuilder[A, i.ListSet](i.ListSet.newBuilder[A])

      case c.Iterable | i.Iterable =>
        new IdentityPreservingBuilder[A, i.Iterable](i.Iterable.newBuilder[A])

      case c.Traversable | i.Traversable =>
        new IdentityPreservingBuilder[A, i.Traversable](i.Traversable.newBuilder[A])

      case _ => fact.newBuilder[A]
    }
    simpleCBF(builder)
  }

  implicit def sortedSetCompanionToCBF[A: Ordering,
                                       CC[X] <: c.SortedSet[X] with c.SortedSetLike[X, CC[X]]](
      fact: SortedSetFactory[CC]): CanBuildFrom[Any, A, CC[A]] = {
    def builder: m.Builder[A, CC[A]] = {
      val b = fact match {
        case c.SortedSet | i.SortedSet =>
          new IdentityPreservingBuilder[A, i.SortedSet](i.SortedSet.newBuilder[A])
        case i.TreeSet =>
          new IdentityPreservingBuilder[A, i.TreeSet](i.TreeSet.newBuilder[A])
        case _ =>
          fact.newBuilder[A]
      }
      // Cast needed because GADT inference doesn't unify CC (didn't dig down why). Example:
      //   def t: CC[A] = fact match { case i.SortedSet => null: i.SortedSet[A] }
      b.asInstanceOf[m.Builder[A, CC[A]]]
    }
    simpleCBF(builder)
  }

  implicit def arrayCompanionToCBF[A: ClassTag](fact: Array.type): CanBuildFrom[Any, A, Array[A]] =
    simpleCBF(Array.newBuilder[A])

  // bounds should be `c.` but binary compatibility
  implicit def mapFactoryToCBF[K,
                               V,
                               CC[A, B] <: /*c.*/ Map[A, B] with /*c.*/ MapLike[A, B, CC[A, B]]](
      fact: MapFactory[CC]): CanBuildFrom[Any, (K, V), CC[K, V]] = {
    def builder: m.Builder[(K, V), CC[K, V]] = {
      val b = fact match {
        case c.Map | i.Map =>
          new IdentityPreservingMapBuilder[K, V, i.Map](i.Map.newBuilder[K, V])
        case i.HashMap =>
          new IdentityPreservingMapBuilder[K, V, i.HashMap](i.HashMap.newBuilder[K, V])
        case i.ListMap =>
          new IdentityPreservingMapBuilder[K, V, i.ListMap](i.ListMap.newBuilder[K, V])
        case _ =>
          fact.newBuilder[K, V]
      }
      // Cast needed because GADT inference doesn't unify CC (didn't dig down why). Example:
      //   def t: CC[K, V] = fact match { case i.Map => null: i.Map[K, V] }
      b.asInstanceOf[m.Builder[(K, V), CC[K, V]]]
    }
    simpleCBF(builder)
  }

  implicit def sortedMapFactoryToCBF[
      K: Ordering,
      V,
      CC[A, B] <: c.SortedMap[A, B] with c.SortedMapLike[A, B, CC[A, B]]](
      fact: SortedMapFactory[CC]): CanBuildFrom[Any, (K, V), CC[K, V]] = {
    def builder: m.Builder[(K, V), CC[K, V]] = {
      val b = fact match {
        case c.SortedMap | i.SortedMap =>
          new IdentityPreservingMapBuilder[K, V, i.SortedMap](i.SortedMap.newBuilder[K, V])
        case i.TreeMap =>
          new IdentityPreservingMapBuilder[K, V, i.TreeMap](i.TreeMap.newBuilder[K, V])
        case _ =>
          fact.newBuilder[K, V]
      }
      b.asInstanceOf[m.Builder[(K, V), CC[K, V]]]
    }
    simpleCBF(builder)
  }

  implicit def bitSetFactoryToCBF(
      fact: BitSetFactory[c.BitSet]): CanBuildFrom[Any, Int, c.BitSet] = {
    def builder: m.Builder[Int, c.BitSet] = fact match {
      case c.BitSet =>
        new IdentityPreservingBitSetBuilder[i.BitSet](i.BitSet.newBuilder)
      case _ =>
        fact.newBuilder
    }
    simpleCBF(builder)
  }

  implicit def immutableBitSetFactoryToCBF(
      fact: BitSetFactory[i.BitSet]): CanBuildFrom[Any, Int, ImmutableBitSetCC[Int]] = {
    def builder: m.Builder[Int, i.BitSet] = fact match {
      case i.BitSet =>
        new IdentityPreservingBitSetBuilder[i.BitSet](i.BitSet.newBuilder)
      case _ =>
        fact.newBuilder
    }
    simpleCBF(builder)
  }

  implicit def mutableBitSetFactoryToCBF(
      fact: BitSetFactory[m.BitSet]): CanBuildFrom[Any, Int, MutableBitSetCC[Int]] =
    simpleCBF(fact.newBuilder)

  implicit class IterableFactoryExtensionMethods[CC[X] <: GenTraversable[X]](
      private val fact: GenericCompanion[CC]) {
    def from[A](source: TraversableOnce[A]): CC[A] =
      fact.apply(source.toSeq: _*)
  }

  implicit def toGenericCompanionExtensionMethods[CC[X] <: GenTraversable[X]](
      companion: GenericCompanion[CC]
  ): GenericCompanionExtensionMethods[CC] = new GenericCompanionExtensionMethods[CC](companion)

  implicit class MapFactoryExtensionMethods[CC[A, B] <: Map[A, B] with MapLike[A, B, CC[A, B]]](
      private val fact: MapFactory[CC]) {
    def from[K, V](source: TraversableOnce[(K, V)]): CC[K, V] =
      fact.apply(source.toSeq: _*)
  }

  implicit class BitSetFactoryExtensionMethods[
      C <: scala.collection.BitSet with scala.collection.BitSetLike[C]](
      private val fact: BitSetFactory[C]) {
    def fromSpecific(source: TraversableOnce[Int]): C =
      fact.apply(source.toSeq: _*)
  }

  private[compat] def build[T, CC](builder: m.Builder[T, CC], source: TraversableOnce[T]): CC = {
    builder ++= source
    builder.result()
  }

  implicit def toImmutableSortedMapExtensions(
      fact: i.SortedMap.type): ImmutableSortedMapExtensions =
    new ImmutableSortedMapExtensions(fact)

  implicit def toImmutableListMapExtensions(fact: i.ListMap.type): ImmutableListMapExtensions =
    new ImmutableListMapExtensions(fact)

  implicit def toImmutableHashMapExtensions(fact: i.HashMap.type): ImmutableHashMapExtensions =
    new ImmutableHashMapExtensions(fact)

  implicit def toImmutableTreeMapExtensions(fact: i.TreeMap.type): ImmutableTreeMapExtensions =
    new ImmutableTreeMapExtensions(fact)

  implicit def toImmutableIntMapExtensions(fact: i.IntMap.type): ImmutableIntMapExtensions =
    new ImmutableIntMapExtensions(fact)

  implicit def toImmutableLongMapExtensions(fact: i.LongMap.type): ImmutableLongMapExtensions =
    new ImmutableLongMapExtensions(fact)

  implicit def toMutableLongMapExtensions(fact: m.LongMap.type): MutableLongMapExtensions =
    new MutableLongMapExtensions(fact)

  implicit def toMutableHashMapExtensions(fact: m.HashMap.type): MutableHashMapExtensions =
    new MutableHashMapExtensions(fact)

  implicit def toMutableListMapExtensions(fact: m.ListMap.type): MutableListMapExtensions =
    new MutableListMapExtensions(fact)

  implicit def toMutableMapExtensions(fact: m.Map.type): MutableMapExtensions =
    new MutableMapExtensions(fact)

  implicit def toStreamExtensionMethods[A](stream: Stream[A]): StreamExtensionMethods[A] =
    new StreamExtensionMethods[A](stream)

  implicit def toSortedExtensionMethods[K, V <: Sorted[K, V]](
      fact: Sorted[K, V]): SortedExtensionMethods[K, V] =
    new SortedExtensionMethods[K, V](fact)

  implicit def toSortedMapExtensionMethods[K, V](
      fact: collection.SortedMap[K, V]): SortedMapExtensionMethods[K, V] =
    new SortedMapExtensionMethods[K, V](fact)

  implicit def toSortedSetExtensionMethods[A](
      fact: collection.SortedSet[A]): SortedSetExtensionMethods[A] =
    new SortedSetExtensionMethods[A](fact)

  implicit def toIteratorExtensionMethods[A](self: Iterator[A]): IteratorExtensionMethods[A] =
    new IteratorExtensionMethods[A](self)

  implicit def toTraversableExtensionMethods[A](
      self: Traversable[A]): TraversableExtensionMethods[A] =
    new TraversableExtensionMethods[A](self)

  implicit def toTraversableOnceExtensionMethods[A](
      self: TraversableOnce[A]): TraversableOnceExtensionMethods[A] =
    new TraversableOnceExtensionMethods[A](self)

  // This really belongs into scala.collection but there's already a package object
  // in scala-library so we can't add to it
  type IterableOnce[+X] = c.TraversableOnce[X]
  val IterableOnce = c.TraversableOnce

  implicit def toMapExtensionMethods[K, V](
      self: scala.collection.Map[K, V]): MapExtensionMethods[K, V] =
    new MapExtensionMethods[K, V](self)

  implicit def toImmutableMapExtensionMethods[K, V](
      self: scala.collection.immutable.Map[K, V]): ImmutableMapExtensionMethods[K, V] =
    new ImmutableMapExtensionMethods[K, V](self)

  implicit def toMutableMapExtensionMethods[K, V](
      self: scala.collection.mutable.Map[K, V]): MutableMapExtensionMethods[K, V] =
    new MutableMapExtensionMethods[K, V](self)

  implicit def toMapViewExtensionMethods[K, V, C <: scala.collection.Map[K, V]](
      self: IterableView[(K, V), C]): MapViewExtensionMethods[K, V, C] =
    new MapViewExtensionMethods[K, V, C](self)

  implicit def toOptionCompanionExtension(fact: Option.type): OptionCompanionExtensionMethods =
    new OptionCompanionExtensionMethods(fact)

  implicit def toRandomExtensions(self: Random): RandomExtensions =
    new RandomExtensions(self)
}

class ImmutableSortedMapExtensions(private val fact: i.SortedMap.type) extends AnyVal {
  def from[K: Ordering, V](source: TraversableOnce[(K, V)]): i.SortedMap[K, V] =
    build(i.SortedMap.newBuilder[K, V], source)
}

class ImmutableListMapExtensions(private val fact: i.ListMap.type) extends AnyVal {
  def from[K, V](source: TraversableOnce[(K, V)]): i.ListMap[K, V] =
    build(i.ListMap.newBuilder[K, V], source)
}

class ImmutableHashMapExtensions(private val fact: i.HashMap.type) extends AnyVal {
  def from[K, V](source: TraversableOnce[(K, V)]): i.HashMap[K, V] =
    build(i.HashMap.newBuilder[K, V], source)
}

class ImmutableTreeMapExtensions(private val fact: i.TreeMap.type) extends AnyVal {
  def from[K: Ordering, V](source: TraversableOnce[(K, V)]): i.TreeMap[K, V] =
    build(i.TreeMap.newBuilder[K, V], source)
}

class ImmutableIntMapExtensions(private val fact: i.IntMap.type) extends AnyVal {
  def from[V](source: TraversableOnce[(Int, V)]): i.IntMap[V] =
    build(i.IntMap.canBuildFrom[Int, V](), source)
}

class ImmutableLongMapExtensions(private val fact: i.LongMap.type) extends AnyVal {
  def from[V](source: TraversableOnce[(Long, V)]): i.LongMap[V] =
    build(i.LongMap.canBuildFrom[Long, V](), source)
}

class MutableLongMapExtensions(private val fact: m.LongMap.type) extends AnyVal {
  def from[V](source: TraversableOnce[(Long, V)]): m.LongMap[V] =
    build(m.LongMap.canBuildFrom[Long, V](), source)
}

class MutableHashMapExtensions(private val fact: m.HashMap.type) extends AnyVal {
  def from[K, V](source: TraversableOnce[(K, V)]): m.HashMap[K, V] =
    build(m.HashMap.canBuildFrom[K, V](), source)
}

class MutableListMapExtensions(private val fact: m.ListMap.type) extends AnyVal {
  def from[K, V](source: TraversableOnce[(K, V)]): m.ListMap[K, V] =
    build(m.ListMap.canBuildFrom[K, V](), source)
}

class MutableMapExtensions(private val fact: m.Map.type) extends AnyVal {
  def from[K, V](source: TraversableOnce[(K, V)]): m.Map[K, V] =
    build(m.Map.canBuildFrom[K, V](), source)
}

class StreamExtensionMethods[A](private val stream: Stream[A]) extends AnyVal {
  def lazyAppendedAll(as: => TraversableOnce[A]): Stream[A] = stream.append(as)
}

class SortedExtensionMethods[K, T <: Sorted[K, T]](private val fact: Sorted[K, T]) {
  def rangeFrom(from: K): T = fact.from(from)
  def rangeTo(to: K): T = fact.to(to)
  def rangeUntil(until: K): T = fact.until(until)
}

class SortedMapExtensionMethods[K, V](
    private val self: collection.SortedMap[K, V]
) extends AnyVal {

  def minAfter(key: K): Option[(K, V)] = self.from(key).headOption

  def maxBefore(key: K): Option[(K, V)] = self.until(key).lastOption
}

class SortedSetExtensionMethods[A](
    private val self: collection.SortedSet[A]
) extends AnyVal {

  def minAfter(key: A): Option[A] = self.from(key).headOption

  def maxBefore(key: A): Option[A] = self.until(key).lastOption
}

class IteratorExtensionMethods[A](private val self: c.Iterator[A]) extends AnyVal {
  def sameElements[B >: A](that: c.TraversableOnce[B]): Boolean = {
    self.sameElements(that.iterator)
  }
  def nextOption(): Option[A] = {
    if (self.hasNext) Some(self.next()) else None
  }
  def concat[B >: A](that: c.TraversableOnce[B]): c.TraversableOnce[B] = self ++ that
  def tapEach[U](f: A => U): c.Iterator[A] = self.map(a => { f(a); a })
}

class TraversableOnceExtensionMethods[A](private val self: c.TraversableOnce[A]) extends AnyVal {
  def iterator: Iterator[A] = self.toIterator

  def minOption[B >: A](implicit ord: Ordering[B]): Option[A] = {
    if (self.isEmpty)
      None
    else
      Some(self.min(ord))
  }

  def maxOption[B >: A](implicit ord: Ordering[B]): Option[A] = {
    if (self.isEmpty)
      None
    else
      Some(self.max(ord))
  }

  def minByOption[B](f: A => B)(implicit cmp: Ordering[B]): Option[A] = {
    if (self.isEmpty)
      None
    else
      Some(self.minBy(f)(cmp))
  }

  def maxByOption[B](f: A => B)(implicit cmp: Ordering[B]): Option[A] = {
    if (self.isEmpty)
      None
    else
      Some(self.maxBy(f)(cmp))
  }
}

class TraversableExtensionMethods[A](private val self: c.Traversable[A]) extends AnyVal {
  def iterableFactory: GenericCompanion[Traversable] = self.companion

  def sizeCompare(otherSize: Int): Int = SizeCompareImpl.sizeCompareInt(self)(otherSize)
  def sizeIs: SizeCompareOps = new SizeCompareOps(self)
  def sizeCompare(that: c.Traversable[_]): Int = SizeCompareImpl.sizeCompareColl(self)(that)

}

class SeqExtensionMethods[A](private val self: c.Seq[A]) extends AnyVal {
  def lengthIs: SizeCompareOps = new SizeCompareOps(self)
}

class SizeCompareOps private[compat] (private val it: c.Traversable[_]) extends AnyVal {
  import SizeCompareImpl._

  /** Tests if the size of the collection is less than some value. */
  @inline def <(size: Int): Boolean = sizeCompareInt(it)(size) < 0

  /** Tests if the size of the collection is less than or equal to some value. */
  @inline def <=(size: Int): Boolean = sizeCompareInt(it)(size) <= 0

  /** Tests if the size of the collection is equal to some value. */
  @inline def ==(size: Int): Boolean = sizeCompareInt(it)(size) == 0

  /** Tests if the size of the collection is not equal to some value. */
  @inline def !=(size: Int): Boolean = sizeCompareInt(it)(size) != 0

  /** Tests if the size of the collection is greater than or equal to some value. */
  @inline def >=(size: Int): Boolean = sizeCompareInt(it)(size) >= 0

  /** Tests if the size of the collection is greater than some value. */
  @inline def >(size: Int): Boolean = sizeCompareInt(it)(size) > 0
}

private object SizeCompareImpl {
  def sizeCompareInt(self: c.Traversable[_])(otherSize: Int): Int =
    self match {
      case self: c.SeqLike[_, _] => self.lengthCompare(otherSize)
      case _ =>
        if (otherSize < 0) 1
        else {
          var i = 0
          val it = self.toIterator
          while (it.hasNext) {
            if (i == otherSize) return 1
            it.next()
            i += 1
          }
          i - otherSize
        }
    }

  // `IndexedSeq` is the only thing that we can safely say has a known size
  def sizeCompareColl(self: c.Traversable[_])(that: c.Traversable[_]): Int =
    that match {
      case that: c.IndexedSeq[_] => sizeCompareInt(self)(that.length)
      case _ =>
        self match {
          case self: c.IndexedSeq[_] =>
            val res = sizeCompareInt(that)(self.length)
            // can't just invert the result, because `-Int.MinValue == Int.MinValue`
            if (res == Int.MinValue) 1 else -res
          case _ =>
            val thisIt = self.toIterator
            val thatIt = that.toIterator
            while (thisIt.hasNext && thatIt.hasNext) {
              thisIt.next()
              thatIt.next()
            }
            java.lang.Boolean.compare(thisIt.hasNext, thatIt.hasNext)
        }
    }
}

class TraversableLikeExtensionMethods[A, Repr](private val self: c.GenTraversableLike[A, Repr])
    extends AnyVal {
  def tapEach[U](f: A => U)(implicit bf: CanBuildFrom[Repr, A, Repr]): Repr =
    self.map(a => { f(a); a })

  def partitionMap[A1, A2, That, Repr1, Repr2](f: A => Either[A1, A2])(
      implicit bf1: CanBuildFrom[Repr, A1, Repr1],
      bf2: CanBuildFrom[Repr, A2, Repr2]
  ): (Repr1, Repr2) = {
    val l = bf1()
    val r = bf2()
    self.foreach { x =>
      f(x) match {
        case Left(x1) => l += x1
        case Right(x2) => r += x2
      }
    }
    (l.result(), r.result())
  }

  def groupMap[K, B, That](key: A => K)(f: A => B)(
      implicit bf: CanBuildFrom[Repr, B, That]): Map[K, That] = {
    val map = m.Map.empty[K, m.Builder[B, That]]
    for (elem <- self) {
      val k = key(elem)
      val bldr = map.getOrElseUpdate(k, bf(self.repr))
      bldr += f(elem)
    }
    val res = Map.newBuilder[K, That]
    for ((k, bldr) <- map) res += ((k, bldr.result()))
    res.result()
  }

  def groupMapReduce[K, B](key: A => K)(f: A => B)(reduce: (B, B) => B): Map[K, B] = {
    val map = m.Map.empty[K, B]
    for (elem <- self) {
      val k = key(elem)
      val v = map.get(k) match {
        case Some(b) => reduce(b, f(elem))
        case None => f(elem)
      }
      map.put(k, v)
    }
    map.toMap
  }

  def distinctBy[B, That](f: A => B)(implicit cbf: CanBuildFrom[Repr, A, That]): That = {
    val builder = cbf()
    val keys = collection.mutable.Set.empty[B]
    for (element <- self) {
      val key = f(element)
      if (!keys.contains(key)) {
        builder += element
        keys += key
      }
    }
    builder.result()
  }
}

class TrulyTraversableLikeExtensionMethods[El1, Repr1](
    private val self: TraversableLike[El1, Repr1])
    extends AnyVal {

  def lazyZip[El2, Repr2, T2](t2: T2)(
      implicit w2: T2 => IterableLike[El2, Repr2]
  ): Tuple2Zipped[El1, Repr1, El2, Repr2] = new Tuple2Zipped((self, t2))
}

class Tuple2ZippedExtensionMethods[El1, Repr1, El2, Repr2](
    private val self: Tuple2Zipped[El1, Repr1, El2, Repr2]) {

  def lazyZip[El3, Repr3, T3](t3: T3)(implicit
  w3: T3 => IterableLike[El3, Repr3]): Tuple3Zipped[El1, Repr1, El2, Repr2, El3, Repr3] =
    new Tuple3Zipped((self.colls._1, self.colls._2, t3))
}

class MapExtensionMethods[K, V](private val self: scala.collection.Map[K, V]) extends AnyVal {

  def foreachEntry[U](f: (K, V) => U): Unit = {
    self.foreach { case (k, v) => f(k, v) }
  }

}

class ImmutableMapExtensionMethods[K, V](private val self: scala.collection.immutable.Map[K, V])
    extends AnyVal {

  def updatedWith[V1 >: V](key: K)(remappingFunction: (Option[V]) => Option[V1]): Map[K, V1] =
    remappingFunction(self.get(key)) match {
      case Some(v) => self.updated(key, v)
      case None => self - key
    }
}

class MutableMapExtensionMethods[K, V](private val self: scala.collection.mutable.Map[K, V])
    extends AnyVal {

  def updateWith(key: K)(remappingFunction: (Option[V]) => Option[V]): Option[V] = {
    val updatedEntry = remappingFunction(self.get(key))
    updatedEntry match {
      case Some(v) => self.update(key, v)
      case None => self.remove(key)
    }
    updatedEntry
  }
}

class MapViewExtensionMethods[K, V, C <: scala.collection.Map[K, V]](
    private val self: IterableView[(K, V), C])
    extends AnyVal {

  def mapValues[W, That](f: V => W)(
      implicit bf: CanBuildFrom[IterableView[(K, V), C], (K, W), That]): That =
    self.map[(K, W), That] { case (k, v) => (k, f(v)) }

  // TODO: Replace the current implementation of `mapValues` with this
  //       after major version bump when bincompat can be broken.
  //       At the same time, remove `canBuildFromIterableViewMapLike`
  /*
  def mapValues[W](f: V => W): IterableView[(K, W), C] =
    // the implementation of `self.map` also casts the result
    self.map({ case (k, v) => (k, f(v)) }).asInstanceOf[IterableView[(K, W), C]]
   */

  def filterKeys(p: K => Boolean): IterableView[(K, V), C] =
    self.filter { case (k, _) => p(k) }
}

class ImmutableQueueExtensionMethods[A](private val self: i.Queue[A]) extends AnyVal {
  def enqueueAll[B >: A](iter: c.Iterable[B]): i.Queue[B] =
    self.enqueue(iter.to[i.Iterable])
}

class MutableQueueExtensionMethods[Element](private val self: m.Queue[Element]) extends AnyVal {
  def enqueueAll(iter: c.Iterable[Element]): Unit =
    self.enqueue(iter.toIndexedSeq: _*)
}

class OptionCompanionExtensionMethods(private val fact: Option.type) extends AnyVal {
  def when[A](cond: Boolean)(a: => A): Option[A] = if (cond) Some(a) else None

  @inline def unless[A](cond: Boolean)(a: => A): Option[A] = when(!cond)(a)
}

class GenericCompanionExtensionMethods[CC[X] <: GenTraversable[X]](
    private val companion: GenericCompanion[CC]) extends AnyVal {
  def unfold[A, S](init: S)(f: S => Option[(A, S)])(
      implicit cbf: CanBuildFrom[CC[A], A, CC[A]]
  ): CC[A] = {
    val builder = cbf()

    @tailrec
    def loop(s1: S): Unit = {
      f(s1) match {
        case Some((a, s2)) =>
          builder += a
          loop(s2)
        case None =>
      }
    }

    loop(init)
    builder.result()
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy