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

scala.collection.MultiSet.scala Maven / Gradle / Ivy

The newest version!
package scala.collection

import scala.util.hashing.MurmurHash3

/**
  * A multiset is a set that can contain multiple occurrences of a same value.
  *
  * @tparam A the element type of the collection
  */
trait MultiSet[A]
  extends Iterable[A]
    with MultiSetOps[A, MultiSet, MultiSet[A]]
    with Equals {

  override protected def className: String = "MultiSet"

  override def iterableFactory: IterableFactory[MultiSet] = MultiSet
  override protected def fromSpecific(coll: IterableOnce[A]): MultiSet[A] = iterableFactory.from(coll)
  override protected def newSpecificBuilder: mutable.Builder[A, MultiSet[A]] = iterableFactory.newBuilder
  override def empty: MultiSet[A] = iterableFactory.empty

  def canEqual(that: Any): Boolean = true

  override def equals(o: Any): Boolean = o match {
    case that: MultiSet[A @unchecked] =>
      (this eq that) ||
        that.canEqual(this) &&
        this.size == that.size && (
          try occurrences.forall { case (elem, n) => that.get(elem) == n }
          catch { case _: ClassCastException => false }
        )
    case _ => false
  }

  override def hashCode(): Int = MurmurHash3.unorderedHash(occurrences, "MultiSet".##)

}

trait MultiSetOps[A, +CC[X] <: MultiSet[X], +C <: MultiSet[A]]
  extends IterableOps[A, CC, C] {

  protected def fromSpecificOccurrences(it: Iterable[(A, Int)]): C =
    fromSpecific(it.view.flatMap { case (e, n) => new View.Fill(n)(e) })

  protected def fromOccurrences[E](it: Iterable[(E, Int)]): CC[E] =
    // Note new MultiSet(it.to(Map)) would be more efficient but would also loose duplicates
    iterableFactory.from(it.view.flatMap { case (e, n) => new View.Fill(n)(e) })

  /**
    * @return All the elements contained in this multiset and their number of occurrences
    */
  def occurrences: Map[A, Int]

  def iterator: Iterator[A] =
    occurrences.iterator.flatMap { case (elem, n) => new View.Fill(n)(elem) }

  /**
    * @return The number of occurrences of `elem` in this multiset
    * @param elem Element to look up
    */
  def get(elem: A): Int = occurrences.getOrElse(elem, 0)

  /**
    * @return Whether `elem` has at least one occurrence in this multiset or not
    * @param elem the element to test
    */
  def contains(elem: A): Boolean = occurrences.contains(elem)

  /**
    * @return a new multiset summing the occurrences of this multiset
    *         with the elements of `that`
    *
    * @param that the collection of elements to add to this multiset
    */
  def concat(that: IterableOnce[A]): C = fromSpecific(that match {
    case that: collection.Iterable[A] => new View.Concat(this, that)
    case _ => iterator.concat(that.iterator)
  })

  /**
    * @return a new multiset summing the occurrences of this multiset
    *         and `that` collection of occurrences
    *
    * @param that the collection of occurrences to add to this multiset
    */
  def concatOccurrences(that: Iterable[(A, Int)]): C =
    fromSpecificOccurrences(new View.Concat(occurrences, that))

  /**
    * @return a new multiset resulting from applying the given function `f`
    *         to each pair of element and its number of occurrences of this
    *         multiset and collecting the results
    * @param f the function to apply
    * @tparam B the element type of the returned collection
    */
  def mapOccurrences[B](f: ((A, Int)) => (B, Int)): CC[B] =
    fromOccurrences(new View.Map(occurrences, f))

  def collectOccurrences[B](pf: PartialFunction[(A, Int), (B, Int)]): CC[B] =
    flatMapOccurrences(kvs =>
      if (pf.isDefinedAt(kvs)) new View.Single(pf(kvs))
      else View.Empty
    )

  /**
    * @return a new multiset resulting from applying the given function `f`
    *         to each pair of element and its number of occurrences of this
    *         multiset and concatenating the results
    * @param f the function to apply
    * @tparam B the element type of the returned collection
    */
  def flatMapOccurrences[B](f: ((A, Int)) => IterableOnce[(B, Int)]): CC[B] =
    fromOccurrences(new View.FlatMap(occurrences, f))

  /**
    * @return a new multiset containing only the occurrences of elements
    *         of this multiset that satisfy the given predicate `p`
    */
  def filterOccurrences(p: ((A, Int)) => Boolean): C =
    fromSpecificOccurrences(new View.Filter(occurrences, p, isFlipped = false))

  // TODO Add more multiset operations like union and intersection

}

object MultiSet extends IterableFactory.Delegate(immutable.MultiSet)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy