From f6cc30445dcd6cd87b02dcacca0ce58354f2551e Mon Sep 17 00:00:00 2001 From: Stefan Zeiger Date: Fri, 31 May 2019 16:19:14 +0200 Subject: [PATCH] Add StringOps#collect methods Overloaded to produce either a `String` or an `IndexedSeq[B]`, just like `map` and `flatMap` --- build.sbt | 8 +++ src/library/scala/collection/StringOps.scala | 49 +++++++++++++++++++ .../scala/collection/StringOpsTest.scala | 5 ++ 3 files changed, 62 insertions(+) diff --git a/build.sbt b/build.sbt index 7bc3b9dfc9e5..3ed404422166 100644 --- a/build.sbt +++ b/build.sbt @@ -90,6 +90,14 @@ val mimaPrereleaseHandlingSettings = Seq( mimaBinaryIssueFilters ++= Seq( // Drop after 2.13.0 is out, whence src/reflect/mima-filters/ takes over. ProblemFilters.exclude[Problem]("scala.reflect.internal.*"), + + + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.StringOps.collect$extension"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.StringOps.collect$extension"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.StringOps.collect"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.StringOps.collect"), + + ), ) diff --git a/src/library/scala/collection/StringOps.scala b/src/library/scala/collection/StringOps.scala index 36a4dd6ac199..610aafb7e7b4 100644 --- a/src/library/scala/collection/StringOps.scala +++ b/src/library/scala/collection/StringOps.scala @@ -248,6 +248,55 @@ final class StringOps(private val s: String) extends AnyVal { sb.toString } + /** Builds a new String by applying a partial function to all chars of this String + * on which the function is defined. + * + * @param pf the partial function which filters and maps the String. + * @return a new String resulting from applying the given partial function + * `pf` to each char on which it is defined and collecting the results. + */ + def collect(pf: PartialFunction[Char, Char]): String = { + var i = 0 + var matched = true + def d(x: Char): Char = { + matched = false + 0 + } + val b = new StringBuilder + while(i < s.length) { + matched = true + val v = pf.applyOrElse(s.charAt(i), d) + if(matched) b += v + i += 1 + } + b.result() + } + + /** Builds a new collection by applying a partial function to all chars of this String + * on which the function is defined. + * + * @param pf the partial function which filters and maps the String. + * @tparam B the element type of the returned collection. + * @return a new collection resulting from applying the given partial function + * `pf` to each char on which it is defined and collecting the results. + */ + def collect[B](pf: PartialFunction[Char, B]): immutable.IndexedSeq[B] = { + var i = 0 + var matched = true + def d(x: Char): B = { + matched = false + null.asInstanceOf[B] + } + val b = immutable.IndexedSeq.newBuilder[B] + while(i < s.length) { + matched = true + val v = pf.applyOrElse(s.charAt(i), d) + if(matched) b += v + i += 1 + } + b.result() + } + /** Returns a new collection containing the chars from this string followed by the elements from the * right hand operand. * diff --git a/test/junit/scala/collection/StringOpsTest.scala b/test/junit/scala/collection/StringOpsTest.scala index 9f3b7fbf2f0c..385f8d8b8a73 100644 --- a/test/junit/scala/collection/StringOpsTest.scala +++ b/test/junit/scala/collection/StringOpsTest.scala @@ -97,4 +97,9 @@ class StringOpsTest { @Test def withFilterAndThenMap(): Unit = { assertEquals("hello".withFilter(_ != 'e').map(_.toUpper), "HLLO") } + + @Test def collect: Unit = { + assertEquals("de", "abcdef".collect { case c @ ('b' | 'c') => (c+2).toChar }) + assertEquals(Seq('d'.toInt, 'e'.toInt), "abcdef".collect { case c @ ('b' | 'c') => (c+2).toInt }) + } }