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

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

package org.specs2
package collection

import scala.collection.{GenSeq, GenIterable}
import org.specs2.internal.scalaz._
import Generator._
import Seqx._


/**
 * This trait provides additional methods on Iterable.
 *
 * It is made public so that user can reuse the sameElementsAsMethod
 */
trait Iterablex {
  /**
   * implicit definition to transform an Iterable to an ExtendedIterable
   */
  implicit def extendIterable[T](xs : GenIterable[T]): ExtendedIterable[T] = new ExtendedIterable(xs)

  /**
   * Additional methods for Iterable objects
   */
  class ExtendedIterable[T](xs: GenIterable[T]) {

    /**
     * @return true if the 2 iterables contain the same elements, in the same order, 
     *         according to a function f
     */
    def isSimilar[S >: T](that: GenIterable[S], f: Function2[T, S, Boolean]): Boolean = {
      val it1 = xs.iterator
      val it2 = that.iterator
      var res = true
      while (res && it1.hasNext && it2.hasNext) {
        res = f(it1.next, it2.next)
      }
      !it1.hasNext && !it2.hasNext && res
    }
    /**
     * @return true if the 2 iterables contain the same elements recursively, in any order
     */
    def sameElementsAs(that: GenIterable[T]): Boolean = sameElementsAs(that, (x, y) => x == y)
    /**
     * @return true if the 2 iterables contain the same elements (according to a comparison function f) recursively, in any order
     */
    def sameElementsAs(that: GenIterable[T], f: (T, T) => Boolean): Boolean = {
      def isNotItsOwnIterable(a: GenIterable[_]) = a.isEmpty || a.iterator.next != a
      def matchTwo(x: T, y: T): Boolean = {
        (x, y) match {
          case (a: GenIterable[_], b: GenIterable[_]) if (isNotItsOwnIterable(a)) =>
            x.asInstanceOf[GenIterable[T]].sameElementsAs(y.asInstanceOf[GenIterable[T]], f)
          case _ => f(x, y)
        }
      }
      val ita = xs.iterator.toList
      val itb = that.iterator.toList
      (ita, itb) match {
        case (Nil, Nil) => true
        case (a: GenIterable[_], b: GenIterable[_]) => {
           (a.headOption.isDefined && b.headOption.isDefined) && {
            val (x, y, resta, restb) = (a.head, b.head, a.drop(1), b.drop(1))
            matchTwo(x, y) && resta.sameElementsAs(restb, f) ||
            resta.exists(matchTwo(_, y)) && restb.exists(matchTwo(x, _)) &&
              resta.removeFirst(matchTwo(_, y)).sameElementsAs(restb.removeFirst(matchTwo(x, _)), f)
          }
        }
        case _ => ita == itb
      }
    }
    /**
     * @return true if the second iterable elements are contained in the first, in order
     */
    def containsInOrder(l: T*): Boolean = {
      val firstList = xs.toList
      val secondList = l.toList
      (firstList, secondList) match {
         case (_, Nil) => true
         case (Nil, _) => false
         case (a :: Nil, b :: Nil) => a == b
         case (a :: firstRest, b :: secondRest) => {
           if (a != b)
             firstRest.containsInOrder(secondList:_*)
           else
             firstRest.containsInOrder(secondRest:_*)
         }
      }
    }
    /**
     * @return the representation of the elements of the iterable using the toString method recursively
     */
    def toDeepString: String = {
      if (!xs.isEmpty && xs == xs.iterator.next)
        xs.toString
      else
        "[" + xs.toList.map { x => 
          x match {
            case i: GenIterable[_] => i.toDeepString
            case _ => x.toString
          }
       }.mkString(", ") + "]"
    }
    /** map the first element with a function */
    def mapFirst(f: T => T): GenSeq[T] = (xs.take(1).map(f) ++ xs.drop(1)).toSeq
    /** map the last element with a function */
    def mapLast(f: T => T): Seq[T] = (xs.seq.dropRight(1) ++ xs.seq.takeRight(1).map(f)).toSeq
    /** reduce a list from left to right */
    def reduceWith[S](reducer: Reducer[T, S]) = FoldlGenerator[Iterable].reduce(reducer, xs.seq)
    /** @return a sequence rotated of a number of elements */
    def rotate(n: Int) = xs.slice(n, xs.size) ++ xs.slice(0, n)
    /** @return a randomly mixed sequence */
    def scramble = {
      val random = new java.util.Random
      // rotate arbitrarily the sequence first then sort randomly
      xs.rotate(random.nextInt(xs.size+1)).seq.toSeq.sortWith((_,_) => random.nextInt(2) > 0)
    }

  }
}

object Iterablex extends Iterablex {
  import scala.collection.SeqLike

  /**
   * extractor object to extract the first element of a sequence
   *
   * l match { case e +: rest => ok }
   */
  object +: {
    def unapply[A, C <: SeqLike[A, C]](seq: C with SeqLike[A, C]) =
      seq.headOption.map(h => (h, seq.tail))
  }

  /**
   * extractor object to extract the last element of a sequence
   *
   * l match { case init :+ last => ok }
   */
  object :+ {
    def unapply[A, C <: SeqLike[A, C]](seq: C with SeqLike[A, C]) =
      seq.headOption.map(h => (seq.init, seq.last ))
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy