From cc47fe2556d3de44dd2b4c92b59c9e0dcc7fab1b Mon Sep 17 00:00:00 2001 From: NthPortal Date: Sat, 18 Jan 2020 13:39:15 -0500 Subject: [PATCH] Allow chaining of filterKeys and mapValues Fix implementation of `IterableView#filterKeys` to not require an implicit CBF, and add a commented-out implementation of `IterableView#mapValues` that does not require an implicit CBF. Add an implicit method to create a CBF so that `IterableView#mapValues` can be chained in front of `IterableView#filterKeys`. --- .../scala/collection/compat/package.scala | 11 +++++++++++ .../scala/collection/compat/PackageShared.scala | 14 +++++++++++--- .../scala/collection/compat/package.scala | 11 +++++++++++ .../scala/test/scala/collection/ViewTest.scala | 7 +++++++ 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/compat/src/main/scala-2.11/scala/collection/compat/package.scala b/compat/src/main/scala-2.11/scala/collection/compat/package.scala index bf713e54..8cdd3c38 100644 --- a/compat/src/main/scala-2.11/scala/collection/compat/package.scala +++ b/compat/src/main/scala-2.11/scala/collection/compat/package.scala @@ -21,6 +21,17 @@ package object compat extends compat.PackageShared { implicit ordering: Ordering[A]): CanBuildFrom[Any, A, CC[A]] = CompatImpl.simpleCBF(fact.newBuilder[A]) + // CanBuildFrom instances for `IterableView[(K, V), Map[K, V]]` that preserve + // the strict type of the view to be `Map` instead of `Iterable` + // Instances produced by this method are used to chain `filterKeys` after `mapValues` + implicit def canBuildFromIterableViewMapLike[K, V, L, W, CC[X, Y] <: Map[X, Y]]: CanBuildFrom[IterableView[(K, V), CC[K, V]], (L, W), IterableView[(L, W), CC[L, W]]] = + new CanBuildFrom[IterableView[(K, V), CC[K, V]], (L, W), IterableView[(L, W), CC[L, W]]] { + // `CanBuildFrom` parameters are used as type constraints, they are not used + // at run-time, hence the dummy builder implementations + def apply(from: IterableView[(K, V), CC[K, V]]) = new TraversableView.NoBuilder + def apply() = new TraversableView.NoBuilder + } + implicit def toTraversableLikeExtensionMethods[Repr](self: Repr)( implicit traversable: IsTraversableLike[Repr]) : TraversableLikeExtensionMethods[traversable.A, Repr] = diff --git a/compat/src/main/scala-2.11_2.12/scala/collection/compat/PackageShared.scala b/compat/src/main/scala-2.11_2.12/scala/collection/compat/PackageShared.scala index 785fbcf4..62f6d728 100644 --- a/compat/src/main/scala-2.11_2.12/scala/collection/compat/PackageShared.scala +++ b/compat/src/main/scala-2.11_2.12/scala/collection/compat/PackageShared.scala @@ -354,7 +354,15 @@ class MapViewExtensionMethods[K, V, C <: scala.collection.Map[K, V]]( implicit bf: CanBuildFrom[IterableView[(K, V), C], (K, W), That]): That = self.map[(K, W), That] { case (k, v) => (k, f(v)) } - def filterKeys[That](p: K => Boolean)( - implicit bf: CanBuildFrom[IterableView[(K, V), C], (K, V), That]): That = - self.collect[(K, V), That] { case (k, v) if p(k) => (k, v) } + // TODO: Replace the current implementation of `mapValues` with this + // after major version bump when bincompat can be broken. + // At the same time, remove `canBuildFromIterableViewMapLike` + /* + def mapValues[W](f: V => W): IterableView[(K, W), C] = + // the implementation of `self.map` also casts the result + self.map({ case (k, v) => (k, f(v)) }).asInstanceOf[IterableView[(K, W), C]] + */ + + def filterKeys(p: K => Boolean): IterableView[(K, V), C] = + self.filter { case (k, _) => p(k) } } diff --git a/compat/src/main/scala-2.12/scala/collection/compat/package.scala b/compat/src/main/scala-2.12/scala/collection/compat/package.scala index c73d66a6..012532d4 100644 --- a/compat/src/main/scala-2.12/scala/collection/compat/package.scala +++ b/compat/src/main/scala-2.12/scala/collection/compat/package.scala @@ -32,6 +32,17 @@ package object compat extends compat.PackageShared { implicit ordering: Ordering[A]): CanBuildFrom[Any, A, CC[A]] = CompatImpl.simpleCBF(fact.newBuilder[A]) + // CanBuildFrom instances for `IterableView[(K, V), Map[K, V]]` that preserve + // the strict type of the view to be `Map` instead of `Iterable` + // Instances produced by this method are used to chain `filterKeys` after `mapValues` + implicit def canBuildFromIterableViewMapLike[K, V, L, W, CC[X, Y] <: Map[X, Y]]: CanBuildFrom[IterableView[(K, V), CC[K, V]], (L, W), IterableView[(L, W), CC[L, W]]] = + new CanBuildFrom[IterableView[(K, V), CC[K, V]], (L, W), IterableView[(L, W), CC[L, W]]] { + // `CanBuildFrom` parameters are used as type constraints, they are not used + // at run-time, hence the dummy builder implementations + def apply(from: IterableView[(K, V), CC[K, V]]) = new TraversableView.NoBuilder + def apply() = new TraversableView.NoBuilder + } + implicit def toTraversableLikeExtensionMethods[Repr](self: Repr)( implicit traversable: IsTraversableLike[Repr]) : TraversableLikeExtensionMethods[traversable.A, Repr] = diff --git a/compat/src/test/scala/test/scala/collection/ViewTest.scala b/compat/src/test/scala/test/scala/collection/ViewTest.scala index 7c150fc4..fd28ce23 100644 --- a/compat/src/test/scala/test/scala/collection/ViewTest.scala +++ b/compat/src/test/scala/test/scala/collection/ViewTest.scala @@ -35,4 +35,11 @@ class ViewTest { assertEquals(oldStyle.toMap, newStyle.toMap) } + @Test + def filterKeysMapValues(): Unit = { + val m = Map("a" -> 1, "b" -> 2, "c" -> 3) + assertEquals(Map(), m.view.filterKeys(_.length > 1).mapValues(_ + 1).toMap) + assertEquals(Map(), m.view.mapValues(_ + 1).filterKeys(_.length > 1).toMap) + } + }