Skip to content

Commit

Permalink
Merge branch 'master' into A_new_dedicated_Cats_ecosystem_page(s)_#3121
Browse files Browse the repository at this point in the history
  • Loading branch information
ebenini-mdsol committed Sep 16, 2020
2 parents 41efe74 + 3179825 commit b8777c9
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version=2.7.0
version=2.7.1
align.openParenCallSite = true
align.openParenDefnSite = true
maxColumn = 120
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ run `sbt docs/makeMicrosite`

2. In a shell, navigate to the generated site directory in `cats-docs/target/site`

3. Start jekyll with `jekyll serve`
3. Start jekyll with `jekyll serve -b /cats`

4. Navigate to http://localhost:4000/cats/ in your browser

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
[![Financial Contributors on Open Collective](https://opencollective.com/typelevel/all/badge.svg?label=financial+contributors)](https://opencollective.com/typelevel) [![Chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/typelevel/cats)
[![codecov.io](http://codecov.io/github/typelevel/cats/coverage.svg?branch=master)](http://codecov.io/github/typelevel/cats?branch=master)
[![Latest version](https://index.scala-lang.org/typelevel/cats/cats-core/latest.svg?color=orange&v=1)](https://index.scala-lang.org/typelevel/cats/cats-core)
[![Scala.js](http://scala-js.org/assets/badges/scalajs-0.6.14.svg)](http://scala-js.org)
[![Scala.js](http://scala-js.org/assets/badges/scalajs-1.2.0.svg)](http://scala-js.org)


### Overview
Expand Down
31 changes: 31 additions & 0 deletions core/src/main/scala/cats/TraverseFilter.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package cats

import cats.data.State
import simulacrum.{noop, typeclass}

import scala.annotation.implicitNotFound
import scala.collection.immutable.{HashSet, TreeSet}

/**
* `TraverseFilter`, also known as `Witherable`, represents list-like structures
Expand Down Expand Up @@ -85,6 +88,32 @@ trait TraverseFilter[F[_]] extends FunctorFilter[F] {

override def mapFilter[A, B](fa: F[A])(f: A => Option[B]): F[B] =
traverseFilter[Id, A, B](fa)(f)

/**
* Removes duplicate elements from a list, keeping only the first occurrence.
*/
def ordDistinct[A](fa: F[A])(implicit O: Order[A]): F[A] = {
implicit val ord: Ordering[A] = O.toOrdering

traverseFilter[State[TreeSet[A], *], A, A](fa)(a =>
State(alreadyIn => if (alreadyIn(a)) (alreadyIn, None) else (alreadyIn + a, Some(a)))
)
.run(TreeSet.empty)
.value
._2
}

/**
* Removes duplicate elements from a list, keeping only the first occurrence.
* This is usually faster than ordDistinct, especially for things that have a slow comparion (like String).
*/
def hashDistinct[A](fa: F[A])(implicit H: Hash[A]): F[A] =
traverseFilter[State[HashSet[A], *], A, A](fa)(a =>
State(alreadyIn => if (alreadyIn(a)) (alreadyIn, None) else (alreadyIn + a, Some(a)))
)
.run(HashSet.empty)
.value
._2
}

object TraverseFilter {
Expand Down Expand Up @@ -119,6 +148,8 @@ object TraverseFilter {
typeClassInstance.filterA[G, A](self)(f)(G)
def traverseEither[G[_], B, C](f: A => G[Either[C, B]])(g: (A, C) => G[Unit])(implicit G: Monad[G]): G[F[B]] =
typeClassInstance.traverseEither[G, A, B, C](self)(f)(g)(G)
def ordDistinct(implicit O: Order[A]): F[A] = typeClassInstance.ordDistinct(self)
def hashDistinct(implicit H: Hash[A]): F[A] = typeClassInstance.hashDistinct(self)
}
trait AllOps[F[_], A] extends Ops[F, A] with FunctorFilter.AllOps[F, A] {
type TypeClassType <: TraverseFilter[F]
Expand Down
2 changes: 1 addition & 1 deletion project/build.sbt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
libraryDependencies += "org.yaml" % "snakeyaml" % "1.26"
libraryDependencies += "org.yaml" % "snakeyaml" % "1.27"
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,4 @@ trait ScalaVersionSpecificTraverseSuite { self: TraverseSuiteAdditional =>

class TraverseLazyListSuite extends TraverseSuite[LazyList]("LazyList")
class TraverseLazyListSuiteUnderlying extends TraverseSuite.Underlying[LazyList]("LazyList")
class TraverseFilterLazyListSuite extends TraverseFilterSuite[LazyList]("LazyList")
43 changes: 43 additions & 0 deletions tests/src/test/scala/cats/tests/TraverseFilterSuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package cats.tests

import cats.data.Chain
import cats.instances.all._
import cats.laws.discipline.arbitrary.catsLawsArbitraryForChain
import cats.syntax.eq._
import cats.syntax.foldable._
import cats.syntax.traverseFilter._
import cats.{Traverse, TraverseFilter}
import org.scalacheck.Arbitrary
import org.scalacheck.Prop.forAll

import scala.collection.immutable.Queue

abstract class TraverseFilterSuite[F[_]: TraverseFilter](name: String)(implicit
ArbFInt: Arbitrary[F[Int]],
ArbFString: Arbitrary[F[String]]
) extends CatsSuite {

implicit def T: Traverse[F] = implicitly[TraverseFilter[F]].traverse

test(s"TraverseFilter[$name].ordDistinct") {
forAll { (fa: F[Int]) =>
fa.ordDistinct.toList === fa.toList.distinct
}
}

test(s"TraverseFilter[$name].hashDistinct") {
forAll { (fa: F[String]) =>
fa.hashDistinct.toList === fa.toList.distinct
}
}
}

class TraverseFilterListSuite extends TraverseFilterSuite[List]("list")

class TraverseFilterVectorSuite extends TraverseFilterSuite[Vector]("vector")

class TraverseFilterChainSuite extends TraverseFilterSuite[Chain]("chain")

class TraverseFilterQueueSuite extends TraverseFilterSuite[Queue]("queue")

class TraverseFilterStreamSuite extends TraverseFilterSuite[Stream]("stream")

0 comments on commit b8777c9

Please sign in to comment.