Skip to content

Commit

Permalink
Merge pull request #4608 from cybersaurus/NonEmptyCollection-distinctBy
Browse files Browse the repository at this point in the history
Add distinctBy to NonEmptyCollection and all impls
  • Loading branch information
satorg authored Jun 6, 2024
2 parents 930f25c + a39d75f commit 17d7e95
Show file tree
Hide file tree
Showing 11 changed files with 76 additions and 24 deletions.
15 changes: 9 additions & 6 deletions core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala
Original file line number Diff line number Diff line change
Expand Up @@ -345,14 +345,17 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A])
/**
* Remove duplicates. Duplicates are checked using `Order[_]` instance.
*/
def distinct[AA >: A](implicit O: Order[AA]): NonEmptyLazyList[AA] = {
implicit val ord: Ordering[AA] = O.toOrdering
override def distinct[AA >: A](implicit O: Order[AA]): NonEmptyLazyList[AA] = distinctBy(identity[AA])

val buf = LazyList.newBuilder[AA]
toLazyList.foldLeft(TreeSet.empty[AA]) { (elementsSoFar, a) =>
if (elementsSoFar(a)) elementsSoFar
override def distinctBy[B](f: A => B)(implicit O: Order[B]): NonEmptyLazyList[A] = {
implicit val ord: Ordering[B] = O.toOrdering

val buf = LazyList.newBuilder[A]
toLazyList.foldLeft(TreeSet.empty[B]) { (elementsSoFar, a) =>
val b = f(a)
if (elementsSoFar(b)) elementsSoFar
else {
buf += a; elementsSoFar + a
buf += a; elementsSoFar + b
}
}

Expand Down
5 changes: 4 additions & 1 deletion core/src/main/scala/cats/data/NonEmptyChain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,10 @@ class NonEmptyChainOps[A](private val value: NonEmptyChain[A])
* Remove duplicates. Duplicates are checked using `Order[_]` instance.
*/
final def distinct[AA >: A](implicit O: Order[AA]): NonEmptyChain[AA] =
create(toChain.distinct[AA])
distinctBy(identity[AA])

final def distinctBy[B](f: A => B)(implicit O: Order[B]): NonEmptyChain[A] =
create(toChain.distinctBy[B](f))

final def sortBy[B](f: A => B)(implicit B: Order[B]): NonEmptyChain[A] = create(toChain.sortBy(f))
final def sorted[AA >: A](implicit AA: Order[AA]): NonEmptyChain[AA] = create(toChain.sorted[AA])
Expand Down
1 change: 1 addition & 0 deletions core/src/main/scala/cats/data/NonEmptyCollection.scala
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ private[cats] trait NonEmptyCollection[+A, U[+_], NE[+_]] extends Any {
def zipWithIndex: NE[(A, Int)]

def distinct[AA >: A](implicit O: Order[AA]): NE[AA]
def distinctBy[B](f: A => B)(implicit O: Order[B]): NE[A]
def sortBy[B](f: A => B)(implicit B: Order[B]): NE[A]
def sorted[AA >: A](implicit AA: Order[AA]): NE[AA]
def groupByNem[B](f: A => B)(implicit B: Order[B]): NonEmptyMap[B, NE[A]]
Expand Down
13 changes: 8 additions & 5 deletions core/src/main/scala/cats/data/NonEmptyList.scala
Original file line number Diff line number Diff line change
Expand Up @@ -340,14 +340,17 @@ final case class NonEmptyList[+A](head: A, tail: List[A]) extends NonEmptyCollec
/**
* Remove duplicates. Duplicates are checked using `Order[_]` instance.
*/
def distinct[AA >: A](implicit O: Order[AA]): NonEmptyList[AA] = {
implicit val ord: Ordering[AA] = O.toOrdering
override def distinct[AA >: A](implicit O: Order[AA]): NonEmptyList[AA] = distinctBy(identity[AA])

val buf = ListBuffer.empty[AA]
tail.foldLeft(TreeSet(head: AA)) { (elementsSoFar, b) =>
override def distinctBy[B](f: A => B)(implicit O: Order[B]): NonEmptyList[A] = {
implicit val ord: Ordering[B] = O.toOrdering

val buf = ListBuffer.empty[A]
tail.foldLeft(TreeSet(f(head): B)) { (elementsSoFar, a) =>
val b = f(a)
if (elementsSoFar(b)) elementsSoFar
else {
buf += b; elementsSoFar + b
buf += a; elementsSoFar + b
}
}

Expand Down
15 changes: 9 additions & 6 deletions core/src/main/scala/cats/data/NonEmptySeq.scala
Original file line number Diff line number Diff line change
Expand Up @@ -236,14 +236,17 @@ final class NonEmptySeq[+A] private (val toSeq: Seq[A]) extends AnyVal with NonE
/**
* Remove duplicates. Duplicates are checked using `Order[_]` instance.
*/
def distinct[AA >: A](implicit O: Order[AA]): NonEmptySeq[AA] = {
implicit val ord: Ordering[AA] = O.toOrdering
override def distinct[AA >: A](implicit O: Order[AA]): NonEmptySeq[AA] = distinctBy(identity[AA])

val buf = Seq.newBuilder[AA]
tail.foldLeft(TreeSet(head: AA)) { (elementsSoFar, a) =>
if (elementsSoFar(a)) elementsSoFar
override def distinctBy[B](f: A => B)(implicit O: Order[B]): NonEmptySeq[A] = {
implicit val ord: Ordering[B] = O.toOrdering

val buf = Seq.newBuilder[A]
tail.foldLeft(TreeSet(f(head): B)) { (elementsSoFar, a) =>
val b = f(a)
if (elementsSoFar(b)) elementsSoFar
else {
buf += a; elementsSoFar + a
buf += a; elementsSoFar + b
}
}

Expand Down
15 changes: 9 additions & 6 deletions core/src/main/scala/cats/data/NonEmptyVector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -246,14 +246,17 @@ final class NonEmptyVector[+A] private (val toVector: Vector[A])
/**
* Remove duplicates. Duplicates are checked using `Order[_]` instance.
*/
def distinct[AA >: A](implicit O: Order[AA]): NonEmptyVector[AA] = {
implicit val ord: Ordering[AA] = O.toOrdering
override def distinct[AA >: A](implicit O: Order[AA]): NonEmptyVector[AA] = distinctBy(identity[AA])

val buf = Vector.newBuilder[AA]
tail.foldLeft(TreeSet(head: AA)) { (elementsSoFar, a) =>
if (elementsSoFar(a)) elementsSoFar
override def distinctBy[B](f: A => B)(implicit O: Order[B]): NonEmptyVector[A] = {
implicit val ord: Ordering[B] = O.toOrdering

val buf = Vector.newBuilder[A]
tail.foldLeft(TreeSet(f(head): B)) { (elementsSoFar, a) =>
val b = f(a)
if (elementsSoFar(b)) elementsSoFar
else {
buf += a; elementsSoFar + a
buf += a; elementsSoFar + b
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ class NonEmptyLazyListSuite extends NonEmptyCollectionSuite[LazyList, NonEmptyLa
}
}

test("NonEmptyLazyList#distinctBy is consistent with List#distinctBy") {
forAll { (ci: NonEmptyLazyList[Int], f: Int => String) =>
assert(ci.distinctBy(f).toList === (ci.toList.distinctBy(f)))
}
}

test("NonEmptyLazyList#distinct is consistent with List#distinct") {
forAll { (ci: NonEmptyLazyList[Int]) =>
assert(ci.distinct.toList === (ci.toList.distinct))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,12 @@ class NonEmptyChainSuite extends NonEmptyCollectionSuite[Chain, NonEmptyChain, N
}
}

test("NonEmptyChain#distinctBy is consistent with List#distinctBy") {
forAll { (ci: NonEmptyChain[Int], f: Int => String) =>
assert(ci.distinctBy(f).toList === (ci.toList.distinctBy(f)))
}
}

test("NonEmptyChain#distinct is consistent with List#distinct") {
forAll { (ci: NonEmptyChain[Int]) =>
assert(ci.distinct.toList === (ci.toList.distinct))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,12 @@ class NonEmptyListSuite extends NonEmptyCollectionSuite[List, NonEmptyList, NonE
}
}

test("NonEmptyList#distinctBy is consistent with List#distinctBy") {
forAll { (nel: NonEmptyList[Int], f: Int => String) =>
assert(nel.distinctBy(f).toList === (nel.toList.distinctBy(f)))
}
}

test("NonEmptyList#reverse is consistent with List#reverse") {
forAll { (nel: NonEmptyList[Int]) =>
assert(nel.reverse.toList === (nel.toList.reverse))
Expand Down
12 changes: 12 additions & 0 deletions tests/shared/src/test/scala/cats/tests/NonEmptySeqSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,16 @@ class NonEmptySeqSuite extends NonEmptyCollectionSuite[Seq, NonEmptySeq, NonEmpt
assert(a.zip(b).toSeq === (a.toSeq.zip(b.toSeq)))
}
}

test("NonEmptySeq#distinct is consistent with List#distinct") {
forAll { (nes: NonEmptySeq[Int]) =>
assert(nes.distinct.toList === (nes.toList.distinct))
}
}

test("NonEmptySeq#distinctBy is consistent with List#distinctBy") {
forAll { (nes: NonEmptySeq[Int], f: Int => String) =>
assert(nes.distinctBy(f).toList === (nes.toList.distinctBy(f)))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,12 @@ class NonEmptyVectorSuite extends NonEmptyCollectionSuite[Vector, NonEmptyVector
assert(compileErrors("val bad: NonEmptyVector[Int] = NonEmptyVector(Vector.empty[Int])").nonEmpty)
}

test("NonEmptyVector#distinctBy is consistent with Vector#distinctBy") {
forAll { (nonEmptyVector: NonEmptyVector[Int], f: Int => String) =>
assert(nonEmptyVector.distinctBy(f).toVector === (nonEmptyVector.toVector.distinctBy(f)))
}
}

test("NonEmptyVector#distinct is consistent with Vector#distinct") {
forAll { (nonEmptyVector: NonEmptyVector[Int]) =>
assert(nonEmptyVector.distinct.toVector === (nonEmptyVector.toVector.distinct))
Expand Down

0 comments on commit 17d7e95

Please sign in to comment.