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

org.specs2.collection.Seqx.scala Maven / Gradle / Ivy

package org.specs2
package collection

import internal.scalaz._
import Generator._

/**
 * This trait provides additional methods on Seqs and nested Seqs
 */
private[specs2]
trait Seqx { outer =>

  /** @return an extension for a nested seq */
  implicit def extendNestedSeq[T](seq: Seq[Seq[T]]): ExtendedNestedSeq[T] = new ExtendedNestedSeq(seq)
  /**
   * Additional methods for nested seqs
   */
  class ExtendedNestedSeq[T](seq: Seq[Seq[T]]) {
    def safeTranspose = outer.transpose(seq)
  }
  /** @return an extension for a seq */
  implicit def extendSeq[T](seq: Seq[T]): ExtendedSeq[T] = new ExtendedSeq(seq)
  /**
   * Additional methods for seqs
   */
  class ExtendedSeq[T](seq: Seq[T]) {

    def reduceWith[S](reducer: Reducer[T, S]) = FoldlGenerator[Seq].reduce(reducer, seq)

    /** update the last element if there is one */
    def updateLast(f: T => T) = seq match {
      case s :+ last => s :+ f(last)
      case other     => other
    }

    /** update the last element or start the sequence with a new init value */
    def updateLastOr(f: PartialFunction[T, T])(initValue: =>T) = seq match {
      case s :+ last => s :+ f(last)
      case other     => seq :+ initValue
    }

    /**
     * remove the first element satisfying the predicate
     * @return a seq minus the first element satisfying the predicate
     */
    def removeFirst(predicate: T => Boolean): Seq[T] = {
      val (withoutElement, startWithElement) = seq span (x => !predicate(x))
      withoutElement ++ startWithElement.drop(1)
    }

    /**
     * @return all the elements in seq which are not in other, even if they are duplicates: Seq(1, 1).diff(Seq(1)) == Seq(1)
     *         this uses a user given comparison function
     */
    def delta[S](other: Seq[S], compare: (T, S) => Boolean): Seq[T] = {
      def notFound(ls1: Seq[T], ls2: Seq[S], result: Seq[T] = Seq()): Seq[T] =
        ls1 match {
          case Seq()        => result
          case head +: rest =>
            if  (ls2.exists(compare(head, _))) notFound(rest, ls2.removeFirst(l => compare(head, l)), result)
            else                               notFound(rest, ls2, result :+ head)
        }
      notFound(seq, other)
    }
  }

  /**
   * This methods works like the transpose method defined on Traversable
   * but it doesn't fail when the input is not formatted like a regular matrix
   *
   *  List(List("a",  "bb", "ccc"),
   *       List("dd", "e",  "fff")) =>
   *  List(List("a",  "dd"),
   *       List("e",  "bb")
   *       List("ccc",  "fff"))
   */
  def transpose[T](xs: Seq[Seq[T]]): Seq[Seq[T]] = {
    val filtered = xs.filter(_.nonEmpty)
    if (filtered.isEmpty) Seq()
    else filtered.map(_.head) +: transpose(filtered.map(_.tail))
  }
}

/**
 * extrator for the first element of Seq[T]
 */
object +: {
  def unapply[T](l: Seq[T]): Option[(T, Seq[T])] = {
    if(l.isEmpty) None
    else          Some(l.head, l.tail)
  }
}

/**
 * extrator for the last element of Seq[T]
 */
object :+ {
  def unapply[T](l: Seq[T]): Option[(Seq[T], T)] = {
    if(l.isEmpty) None
    else          Some(l.init, l.last)
  }
}

private[specs2]
object Seqx extends Seqx




© 2015 - 2025 Weber Informatics LLC | Privacy Policy