Skip to content

Commit e9b342f

Browse files
committed
Add distinctBy to NonEmptyCollection and all impls
1 parent 502e4b8 commit e9b342f

File tree

11 files changed

+73
-20
lines changed

11 files changed

+73
-20
lines changed

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

+8-5
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])
349+
350+
override def distinctBy[AA >: A, B](f: A => B)(implicit O: Order[B]): NonEmptyLazyList[AA] = {
351+
implicit val ord: Ordering[B] = O.toOrdering
350352

351353
val buf = LazyList.newBuilder[AA]
352-
toLazyList.foldLeft(TreeSet.empty[AA]) { (elementsSoFar, a) =>
353-
if (elementsSoFar(a)) elementsSoFar
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[AA >: A, B](f: A => B)(implicit O: Order[B]): NonEmptyChain[AA] =
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[AA >: A, B](f: A => B)(implicit O: Order[B]): NE[AA]
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

+7-4
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])
344+
345+
override def distinctBy[AA >: A, B](f: A => B)(implicit O: Order[B]): NonEmptyList[AA] = {
346+
implicit val ord: Ordering[B] = O.toOrdering
345347

346348
val buf = ListBuffer.empty[AA]
347-
tail.foldLeft(TreeSet(head: AA)) { (elementsSoFar, b) =>
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

+8-5
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])
240+
241+
override def distinctBy[AA >: A, B](f: A => B)(implicit O: Order[B]): NonEmptySeq[AA] = {
242+
implicit val ord: Ordering[B] = O.toOrdering
241243

242244
val buf = Seq.newBuilder[AA]
243-
tail.foldLeft(TreeSet(head: AA)) { (elementsSoFar, a) =>
244-
if (elementsSoFar(a)) elementsSoFar
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

+8-5
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])
250+
251+
override def distinctBy[AA >: A, B](f: A => B)(implicit O: Order[B]): NonEmptyVector[AA] = {
252+
implicit val ord: Ordering[B] = O.toOrdering
251253

252254
val buf = Vector.newBuilder[AA]
253-
tail.foldLeft(TreeSet(head: AA)) { (elementsSoFar, a) =>
254-
if (elementsSoFar(a)) elementsSoFar
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

+7
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class NonEmptyLazyListSuite extends NonEmptyCollectionSuite[LazyList, NonEmptyLa
4949
checkAll(s"NonEmptyLazyList[Int]", HashTests[NonEmptyLazyList[Int]].hash)
5050
checkAll(s"Hash[NonEmptyLazyList[Int]]", SerializableTests.serializable(Hash[NonEmptyLazyList[Int]]))
5151

52+
checkAll("NonEmptyLazyList[Int]", NonEmptyAlternativeTests[NonEmptyLazyList].nonEmptyAlternative[Int, Int, Int])
5253
checkAll("NonEmptyLazyList[Int]", NonEmptyAlternativeTests[NonEmptyLazyList].nonEmptyAlternative[Int, Int, Int])
5354
checkAll("NonEmptyAlternative[NonEmptyLazyList]",
5455
SerializableTests.serializable(NonEmptyAlternative[NonEmptyLazyList])
@@ -175,6 +176,12 @@ class NonEmptyLazyListSuite extends NonEmptyCollectionSuite[LazyList, NonEmptyLa
175176
}
176177
}
177178

179+
test("NonEmptyLazyList#distinctBy is consistent with List#distinctBy") {
180+
forAll { (ci: NonEmptyLazyList[Int], f: Int => String) =>
181+
assert(ci.distinctBy(f).toList === (ci.toList.distinctBy(f)))
182+
}
183+
}
184+
178185
test("NonEmptyLazyList#distinct is consistent with List#distinct") {
179186
forAll { (ci: NonEmptyLazyList[Int]) =>
180187
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)