From 57f388ae94b73c75314ba81587fbf73407ee86ce Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Fri, 28 Jul 2023 16:33:17 +0800 Subject: [PATCH 1/2] Add Stream.filterNot --- core/shared/src/main/scala/fs2/Chunk.scala | 3 ++ core/shared/src/main/scala/fs2/Stream.scala | 4 +++ .../scala/fs2/StreamCombinatorsSuite.scala | 34 +++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/core/shared/src/main/scala/fs2/Chunk.scala b/core/shared/src/main/scala/fs2/Chunk.scala index df74ffecd1..e48d1db308 100644 --- a/core/shared/src/main/scala/fs2/Chunk.scala +++ b/core/shared/src/main/scala/fs2/Chunk.scala @@ -120,6 +120,9 @@ abstract class Chunk[+O] extends Serializable with ChunkPlatform[O] with ChunkRu Chunk.array(b.result()).asInstanceOf[Chunk[O]] } + /** Returns a chunk that has only the elements that do not satisfy the supplied predicate. */ + def filterNot(p: O => Boolean): Chunk[O] = filter(p.andThen(!_)) + /** Returns the first element for which the predicate returns true or `None` if no elements satisfy the predicate. */ def find(p: O => Boolean): Option[O] = iterator.find(p) diff --git a/core/shared/src/main/scala/fs2/Stream.scala b/core/shared/src/main/scala/fs2/Stream.scala index 1ab0dc3091..5120b635f6 100644 --- a/core/shared/src/main/scala/fs2/Stream.scala +++ b/core/shared/src/main/scala/fs2/Stream.scala @@ -1082,6 +1082,10 @@ final class Stream[+F[_], +O] private[fs2] (private[fs2] val underlying: Pull[F, */ def filter(p: O => Boolean): Stream[F, O] = mapChunks(_.filter(p)) + /** Emits only inputs which do not match the supplied predicate. + */ + def filterNot(p: O => Boolean): Stream[F, O] = mapChunks(_.filterNot(p)) + /** Like `filter`, but allows filtering based on an effect. * * Note: The result Stream will consist of chunks that are empty or 1-element-long. diff --git a/core/shared/src/test/scala/fs2/StreamCombinatorsSuite.scala b/core/shared/src/test/scala/fs2/StreamCombinatorsSuite.scala index 0019e25f26..680c494e3e 100644 --- a/core/shared/src/test/scala/fs2/StreamCombinatorsSuite.scala +++ b/core/shared/src/test/scala/fs2/StreamCombinatorsSuite.scala @@ -567,6 +567,40 @@ class StreamCombinatorsSuite extends Fs2Suite { } } + group("filterNot") { + property("1") { + forAll { (s: Stream[Pure, Int], n0: Int) => + val n = (n0 % 20).abs + 1 + val predicate = (i: Int) => i % n == 0 + s.filterNot(predicate).assertEmits(s.toList.filterNot(predicate)) + } + } + + property("2") { + forAll { (s: Stream[Pure, Double]) => + val predicate = (i: Double) => i - i.floor < 0.5 + val s2 = s.mapChunks(c => Chunk.array(c.toArray)) + assertEquals(s2.filterNot(predicate).toList, s2.toList.filterNot(predicate)) + } + } + + property("3") { + forAll { (s: Stream[Pure, Byte]) => + val predicate = (b: Byte) => b < 0 + val s2 = s.mapChunks(c => Chunk.array(c.toArray)) + s2.filterNot(predicate).assertEmits(s2.toList.filterNot(predicate)) + } + } + + property("4") { + forAll { (s: Stream[Pure, Boolean]) => + val predicate = (b: Boolean) => !b + val s2 = s.mapChunks(c => Chunk.array(c.toArray)) + s2.filterNot(predicate).assertEmits(s2.toList.filterNot(predicate)) + } + } + } + property("find") { forAll { (s: Stream[Pure, Int], i: Int) => val predicate = (item: Int) => item < i From 03ad1e338bfb362e6703f854f9b3c061a0eb7c70 Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Sun, 30 Jul 2023 11:14:50 +0800 Subject: [PATCH 2/2] Update core/shared/src/main/scala/fs2/Chunk.scala Co-authored-by: Sergey Torgashov --- core/shared/src/main/scala/fs2/Chunk.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/shared/src/main/scala/fs2/Chunk.scala b/core/shared/src/main/scala/fs2/Chunk.scala index e48d1db308..9e65bee0cf 100644 --- a/core/shared/src/main/scala/fs2/Chunk.scala +++ b/core/shared/src/main/scala/fs2/Chunk.scala @@ -121,7 +121,7 @@ abstract class Chunk[+O] extends Serializable with ChunkPlatform[O] with ChunkRu } /** Returns a chunk that has only the elements that do not satisfy the supplied predicate. */ - def filterNot(p: O => Boolean): Chunk[O] = filter(p.andThen(!_)) + def filterNot(p: O => Boolean): Chunk[O] = filter(!p(_)) /** Returns the first element for which the predicate returns true or `None` if no elements satisfy the predicate. */ def find(p: O => Boolean): Option[O] =