Skip to content

Commit

Permalink
Add distinct method on NonEmptyList and NonEmptyVector (#1243)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tvaroh authored and johnynek committed Aug 12, 2016
1 parent 3fb3ed3 commit cb7e2df
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 1 deletion.
15 changes: 15 additions & 0 deletions core/src/main/scala/cats/data/NonEmptyList.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import cats.instances.list._
import cats.syntax.order._

import scala.annotation.tailrec
import scala.collection.immutable.TreeSet
import scala.collection.mutable.ListBuffer

/**
Expand Down Expand Up @@ -106,6 +107,20 @@ final case class NonEmptyList[A](head: A, tail: List[A]) {
toList.iterator.map(A.show).mkString("NonEmptyList(", ", ", ")")

override def toString: String = s"NonEmpty$toList"

/**
* Remove duplicates. Duplicates are checked using `Order[_]` instance.
*/
def distinct(implicit O: Order[A]): NonEmptyList[A] = {
implicit val ord = O.toOrdering

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

NonEmptyList(head, buf.toList)
}
}

object NonEmptyList extends NonEmptyListInstances {
Expand Down
16 changes: 15 additions & 1 deletion core/src/main/scala/cats/data/NonEmptyVector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package cats
package data

import scala.annotation.tailrec
import scala.collection.immutable.VectorBuilder
import scala.collection.immutable.{TreeSet, VectorBuilder}
import cats.instances.vector._

/**
Expand Down Expand Up @@ -130,6 +130,20 @@ final class NonEmptyVector[A] private (val toVector: Vector[A]) extends AnyVal {
def length: Int = toVector.length

override def toString: String = s"NonEmpty${toVector.toString}"

/**
* Remove duplicates. Duplicates are checked using `Order[_]` instance.
*/
def distinct(implicit O: Order[A]): NonEmptyVector[A] = {
implicit val ord = O.toOrdering

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

NonEmptyVector(head, buf.result())
}
}

private[data] sealed trait NonEmptyVectorInstances {
Expand Down
6 changes: 6 additions & 0 deletions tests/src/test/scala/cats/tests/NonEmptyListTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,12 @@ class NonEmptyListTests extends CatsSuite {
(i :: nel).toList should === (i :: nel.toList)
}
}

test("NonEmptyList#distinct is consistent with List#distinct") {
forAll { nel: NonEmptyList[Int] =>
nel.distinct.toList should === (nel.toList.distinct)
}
}
}

class ReducibleNonEmptyListCheck extends ReducibleCheck[NonEmptyList]("NonEmptyList") {
Expand Down
6 changes: 6 additions & 0 deletions tests/src/test/scala/cats/tests/NonEmptyVectorTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,12 @@ class NonEmptyVectorTests extends CatsSuite {
test("Cannot create a new NonEmptyVector[Int] from apply with a an empty Vector") {
"val bad: NonEmptyVector[Int] = NonEmptyVector(Vector.empty[Int])" shouldNot compile
}

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

class ReducibleNonEmptyVectorCheck extends ReducibleCheck[NonEmptyVector]("NonEmptyVector") {
Expand Down

0 comments on commit cb7e2df

Please sign in to comment.