Skip to content

Commit 17d7e95

Browse files
authored
Merge pull request #4608 from cybersaurus/NonEmptyCollection-distinctBy
Add distinctBy to NonEmptyCollection and all impls
2 parents 930f25c + a39d75f commit 17d7e95

File tree

11 files changed

+76
-24
lines changed

11 files changed

+76
-24
lines changed

core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala

+9-6
Original file line numberDiff line numberDiff line change
@@ -345,14 +345,17 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A])
345345
/**
346346
* Remove duplicates. Duplicates are checked using `Order[_]` instance.
347347
*/
348-
def distinct[AA >: A](implicit O: Order[AA]): NonEmptyLazyList[AA] = {
349-
implicit val ord: Ordering[AA] = O.toOrdering
348+
override def distinct[AA >: A](implicit O: Order[AA]): NonEmptyLazyList[AA] = distinctBy(identity[AA])
350349

351-
val buf = LazyList.newBuilder[AA]
352-
toLazyList.foldLeft(TreeSet.empty[AA]) { (elementsSoFar, a) =>
353-
if (elementsSoFar(a)) elementsSoFar
350+
override def distinctBy[B](f: A => B)(implicit O: Order[B]): NonEmptyLazyList[A] = {
351+
implicit val ord: Ordering[B] = O.toOrdering
352+
353+
val buf = LazyList.newBuilder[A]
354+
toLazyList.foldLeft(TreeSet.empty[B]) { (elementsSoFar, a) =>
355+
val b = f(a)
356+
if (elementsSoFar(b)) elementsSoFar
354357
else {
355-
buf += a; elementsSoFar + a
358+
buf += a; elementsSoFar + b
356359
}
357360
}
358361

core/src/main/scala/cats/data/NonEmptyChain.scala

+4-1
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,10 @@ class NonEmptyChainOps[A](private val value: NonEmptyChain[A])
603603
* Remove duplicates. Duplicates are checked using `Order[_]` instance.
604604
*/
605605
final def distinct[AA >: A](implicit O: Order[AA]): NonEmptyChain[AA] =
606-
create(toChain.distinct[AA])
606+
distinctBy(identity[AA])
607+
608+
final def distinctBy[B](f: A => B)(implicit O: Order[B]): NonEmptyChain[A] =
609+
create(toChain.distinctBy[B](f))
607610

608611
final def sortBy[B](f: A => B)(implicit B: Order[B]): NonEmptyChain[A] = create(toChain.sortBy(f))
609612
final def sorted[AA >: A](implicit AA: Order[AA]): NonEmptyChain[AA] = create(toChain.sorted[AA])

core/src/main/scala/cats/data/NonEmptyCollection.scala

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ private[cats] trait NonEmptyCollection[+A, U[+_], NE[+_]] extends Any {
5252
def zipWithIndex: NE[(A, Int)]
5353

5454
def distinct[AA >: A](implicit O: Order[AA]): NE[AA]
55+
def distinctBy[B](f: A => B)(implicit O: Order[B]): NE[A]
5556
def sortBy[B](f: A => B)(implicit B: Order[B]): NE[A]
5657
def sorted[AA >: A](implicit AA: Order[AA]): NE[AA]
5758
def groupByNem[B](f: A => B)(implicit B: Order[B]): NonEmptyMap[B, NE[A]]

core/src/main/scala/cats/data/NonEmptyList.scala

+8-5
Original file line numberDiff line numberDiff line change
@@ -340,14 +340,17 @@ final case class NonEmptyList[+A](head: A, tail: List[A]) extends NonEmptyCollec
340340
/**
341341
* Remove duplicates. Duplicates are checked using `Order[_]` instance.
342342
*/
343-
def distinct[AA >: A](implicit O: Order[AA]): NonEmptyList[AA] = {
344-
implicit val ord: Ordering[AA] = O.toOrdering
343+
override def distinct[AA >: A](implicit O: Order[AA]): NonEmptyList[AA] = distinctBy(identity[AA])
345344

346-
val buf = ListBuffer.empty[AA]
347-
tail.foldLeft(TreeSet(head: AA)) { (elementsSoFar, b) =>
345+
override def distinctBy[B](f: A => B)(implicit O: Order[B]): NonEmptyList[A] = {
346+
implicit val ord: Ordering[B] = O.toOrdering
347+
348+
val buf = ListBuffer.empty[A]
349+
tail.foldLeft(TreeSet(f(head): B)) { (elementsSoFar, a) =>
350+
val b = f(a)
348351
if (elementsSoFar(b)) elementsSoFar
349352
else {
350-
buf += b; elementsSoFar + b
353+
buf += a; elementsSoFar + b
351354
}
352355
}
353356

core/src/main/scala/cats/data/NonEmptySeq.scala

+9-6
Original file line numberDiff line numberDiff line change
@@ -236,14 +236,17 @@ final class NonEmptySeq[+A] private (val toSeq: Seq[A]) extends AnyVal with NonE
236236
/**
237237
* Remove duplicates. Duplicates are checked using `Order[_]` instance.
238238
*/
239-
def distinct[AA >: A](implicit O: Order[AA]): NonEmptySeq[AA] = {
240-
implicit val ord: Ordering[AA] = O.toOrdering
239+
override def distinct[AA >: A](implicit O: Order[AA]): NonEmptySeq[AA] = distinctBy(identity[AA])
241240

242-
val buf = Seq.newBuilder[AA]
243-
tail.foldLeft(TreeSet(head: AA)) { (elementsSoFar, a) =>
244-
if (elementsSoFar(a)) elementsSoFar
241+
override def distinctBy[B](f: A => B)(implicit O: Order[B]): NonEmptySeq[A] = {
242+
implicit val ord: Ordering[B] = O.toOrdering
243+
244+
val buf = Seq.newBuilder[A]
245+
tail.foldLeft(TreeSet(f(head): B)) { (elementsSoFar, a) =>
246+
val b = f(a)
247+
if (elementsSoFar(b)) elementsSoFar
245248
else {
246-
buf += a; elementsSoFar + a
249+
buf += a; elementsSoFar + b
247250
}
248251
}
249252

core/src/main/scala/cats/data/NonEmptyVector.scala

+9-6
Original file line numberDiff line numberDiff line change
@@ -246,14 +246,17 @@ final class NonEmptyVector[+A] private (val toVector: Vector[A])
246246
/**
247247
* Remove duplicates. Duplicates are checked using `Order[_]` instance.
248248
*/
249-
def distinct[AA >: A](implicit O: Order[AA]): NonEmptyVector[AA] = {
250-
implicit val ord: Ordering[AA] = O.toOrdering
249+
override def distinct[AA >: A](implicit O: Order[AA]): NonEmptyVector[AA] = distinctBy(identity[AA])
251250

252-
val buf = Vector.newBuilder[AA]
253-
tail.foldLeft(TreeSet(head: AA)) { (elementsSoFar, a) =>
254-
if (elementsSoFar(a)) elementsSoFar
251+
override def distinctBy[B](f: A => B)(implicit O: Order[B]): NonEmptyVector[A] = {
252+
implicit val ord: Ordering[B] = O.toOrdering
253+
254+
val buf = Vector.newBuilder[A]
255+
tail.foldLeft(TreeSet(f(head): B)) { (elementsSoFar, a) =>
256+
val b = f(a)
257+
if (elementsSoFar(b)) elementsSoFar
255258
else {
256-
buf += a; elementsSoFar + a
259+
buf += a; elementsSoFar + b
257260
}
258261
}
259262

tests/shared/src/test/scala-2.13+/cats/tests/NonEmptyLazyListSuite.scala

+6
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,12 @@ class NonEmptyLazyListSuite extends NonEmptyCollectionSuite[LazyList, NonEmptyLa
175175
}
176176
}
177177

178+
test("NonEmptyLazyList#distinctBy is consistent with List#distinctBy") {
179+
forAll { (ci: NonEmptyLazyList[Int], f: Int => String) =>
180+
assert(ci.distinctBy(f).toList === (ci.toList.distinctBy(f)))
181+
}
182+
}
183+
178184
test("NonEmptyLazyList#distinct is consistent with List#distinct") {
179185
forAll { (ci: NonEmptyLazyList[Int]) =>
180186
assert(ci.distinct.toList === (ci.toList.distinct))

tests/shared/src/test/scala/cats/tests/NonEmptyChainSuite.scala

+6
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,12 @@ class NonEmptyChainSuite extends NonEmptyCollectionSuite[Chain, NonEmptyChain, N
203203
}
204204
}
205205

206+
test("NonEmptyChain#distinctBy is consistent with List#distinctBy") {
207+
forAll { (ci: NonEmptyChain[Int], f: Int => String) =>
208+
assert(ci.distinctBy(f).toList === (ci.toList.distinctBy(f)))
209+
}
210+
}
211+
206212
test("NonEmptyChain#distinct is consistent with List#distinct") {
207213
forAll { (ci: NonEmptyChain[Int]) =>
208214
assert(ci.distinct.toList === (ci.toList.distinct))

tests/shared/src/test/scala/cats/tests/NonEmptyListSuite.scala

+6
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,12 @@ class NonEmptyListSuite extends NonEmptyCollectionSuite[List, NonEmptyList, NonE
296296
}
297297
}
298298

299+
test("NonEmptyList#distinctBy is consistent with List#distinctBy") {
300+
forAll { (nel: NonEmptyList[Int], f: Int => String) =>
301+
assert(nel.distinctBy(f).toList === (nel.toList.distinctBy(f)))
302+
}
303+
}
304+
299305
test("NonEmptyList#reverse is consistent with List#reverse") {
300306
forAll { (nel: NonEmptyList[Int]) =>
301307
assert(nel.reverse.toList === (nel.toList.reverse))

tests/shared/src/test/scala/cats/tests/NonEmptySeqSuite.scala

+12
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,16 @@ class NonEmptySeqSuite extends NonEmptyCollectionSuite[Seq, NonEmptySeq, NonEmpt
9090
assert(a.zip(b).toSeq === (a.toSeq.zip(b.toSeq)))
9191
}
9292
}
93+
94+
test("NonEmptySeq#distinct is consistent with List#distinct") {
95+
forAll { (nes: NonEmptySeq[Int]) =>
96+
assert(nes.distinct.toList === (nes.toList.distinct))
97+
}
98+
}
99+
100+
test("NonEmptySeq#distinctBy is consistent with List#distinctBy") {
101+
forAll { (nes: NonEmptySeq[Int], f: Int => String) =>
102+
assert(nes.distinctBy(f).toList === (nes.toList.distinctBy(f)))
103+
}
104+
}
93105
}

tests/shared/src/test/scala/cats/tests/NonEmptyVectorSuite.scala

+6
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,12 @@ class NonEmptyVectorSuite extends NonEmptyCollectionSuite[Vector, NonEmptyVector
359359
assert(compileErrors("val bad: NonEmptyVector[Int] = NonEmptyVector(Vector.empty[Int])").nonEmpty)
360360
}
361361

362+
test("NonEmptyVector#distinctBy is consistent with Vector#distinctBy") {
363+
forAll { (nonEmptyVector: NonEmptyVector[Int], f: Int => String) =>
364+
assert(nonEmptyVector.distinctBy(f).toVector === (nonEmptyVector.toVector.distinctBy(f)))
365+
}
366+
}
367+
362368
test("NonEmptyVector#distinct is consistent with Vector#distinct") {
363369
forAll { (nonEmptyVector: NonEmptyVector[Int]) =>
364370
assert(nonEmptyVector.distinct.toVector === (nonEmptyVector.toVector.distinct))

0 commit comments

Comments
 (0)