Skip to content

Commit

Permalink
Merge pull request #1167 from ceedubs/max-min
Browse files Browse the repository at this point in the history
Add max/min and reduceOption methods
  • Loading branch information
peterneyens authored Jul 21, 2016
2 parents a1d76ee + de06e65 commit 611a30c
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 0 deletions.
84 changes: 84 additions & 0 deletions core/src/main/scala/cats/Foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,90 @@ import simulacrum.typeclass
}
}

/**
* Reduce the elements of this structure down to a single value by applying
* the provided aggregation function in a left-associative manner.
*
* @return `None` if the structure is empty, otherwise the result of combining
* the cumulative left-associative result of the `f` operation over all of the
* elements.
*
* @see [[reduceRightOption]] for a right-associative alternative.
*
* @see [[Reducible#reduceLeft]] for a version that doesn't need to return an
* `Option` for structures that are guaranteed to be non-empty.
*
* Example:
* {{{
* scala> import cats.implicits._
* scala> val l = List(6, 3, 2)
* This is equivalent to (6 - 3) - 2
* scala> Foldable[List].reduceLeftOption(l)(_ - _)
* res0: Option[Int] = Some(1)
*
* scala> Foldable[List].reduceLeftOption(List.empty[Int])(_ - _)
* res1: Option[Int] = None
* }}}
*/
def reduceLeftOption[A](fa: F[A])(f: (A, A) => A): Option[A] =
reduceLeftToOption(fa)(identity)(f)

/**
* Reduce the elements of this structure down to a single value by applying
* the provided aggregation function in a right-associative manner.
*
* @return `None` if the structure is empty, otherwise the result of combining
* the cumulative right-associative result of the `f` operation over the
* `A` elements.
*
* @see [[reduceLeftOption]] for a left-associative alternative
*
* @see [[Reducible#reduceRight]] for a version that doesn't need to return an
* `Option` for structures that are guaranteed to be non-empty.
*
* Example:
* {{{
* scala> import cats.implicits._
* scala> val l = List(6, 3, 2)
* This is eqivalent to 6 - (3 - 2)
* scala> Foldable[List].reduceRightOption(l)((current, rest) => rest.map(current - _)).value
* res0: Option[Int] = Some(5)
*
* scala> Foldable[List].reduceRightOption(List.empty[Int])((current, rest) => rest.map(current - _)).value
* res1: Option[Int] = None
* }}}
*/
def reduceRightOption[A](fa: F[A])(f: (A, Eval[A]) => Eval[A]): Eval[Option[A]] =
reduceRightToOption(fa)(identity)(f)

/**
* Find the minimum `A` item in this structure according to the `Order[A]`.
*
* @return `None` if the structure is empty, otherwise the minimum element
* wrapped in a `Some`.
*
* @see [[Reducible#minimum]] for a version that doesn't need to return an
* `Option` for structures that are guaranteed to be non-empty.
*
* @see [[maximumOption]] for maximum instead of minimum.
*/
def minimumOption[A](fa: F[A])(implicit A: Order[A]): Option[A] =
reduceLeftOption(fa)(A.min)

/**
* Find the maximum `A` item in this structure according to the `Order[A]`.
*
* @return `None` if the structure is empty, otherwise the maximum element
* wrapped in a `Some`.
*
* @see [[Reducible#maximum]] for a version that doesn't need to return an
* `Option` for structures that are guaranteed to be non-empty.
*
* @see [[minimumOption]] for minimum instead of maximum.
*/
def maximumOption[A](fa: F[A])(implicit A: Order[A]): Option[A] =
reduceLeftOption(fa)(A.max)

/**
* The size of this Foldable.
*
Expand Down
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/Reducible.scala
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ import simulacrum.typeclass
val F = self
val G = Reducible[G]
}

def minimum[A](fa: F[A])(implicit A: Order[A]): A =
reduceLeft(fa)(A.min)

def maximum[A](fa: F[A])(implicit A: Order[A]): A =
reduceLeft(fa)(A.max)
}

/**
Expand Down
23 changes: 23 additions & 0 deletions tests/src/test/scala/cats/tests/FoldableTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,29 @@ abstract class FoldableCheck[F[_]: Foldable](name: String)(implicit ArbFInt: Arb
fa.nonEmpty should === (iterator(fa).nonEmpty)
}
}

test("maximum/minimum") {
forAll { (fa: F[Int]) =>
val maxOpt = fa.maximumOption
val minOpt = fa.minimumOption
val list = fa.toList
val nelOpt = list.toNel
maxOpt should === (nelOpt.map(_.maximum))
maxOpt should === (nelOpt.map(_.unwrap.max))
minOpt should === (nelOpt.map(_.minimum))
minOpt should === (nelOpt.map(_.unwrap.min))
maxOpt.forall(i => fa.forall(_ <= i)) should === (true)
minOpt.forall(i => fa.forall(_ >= i)) should === (true)
}
}

test("reduceLeftOption/reduceRightOption") {
forAll { (fa: F[Int]) =>
val list = fa.toList
fa.reduceLeftOption(_ - _) should === (list.reduceLeftOption(_ - _))
fa.reduceRightOption((x, ly) => ly.map(x - _)).value should === (list.reduceRightOption(_ - _))
}
}
}

class FoldableTestsAdditional extends CatsSuite {
Expand Down

0 comments on commit 611a30c

Please sign in to comment.