0

Following code does not compile with Scala 2.13.6:

val a = Map(0 -> "0")
val b = Map(1 -> "1")

val c = a.view ++ b.view

c.contains(0)

The error is:

value contains is not a member of scala.collection.View[(Int, String)]

A similar error is shown in Scala 3 or Scala 2.12.15.

I find this unexpected, as implementation of concat seem to suggest the result should be a map (mapFactory is used to produce the result).

How can I concatenate two MapViews to get a MapView again?

Suma
  • 33,181
  • 16
  • 123
  • 191
  • Try to add `toMap`. Take a look at [MapView() in Scala](https://stackoverflow.com/a/65290312/2359227) – Tomer Shetah Sep 20 '21 at 09:44
  • Well, my result is not MapView(not computed), but View(not computed). Using toMap would defeat the purpose of using a view in the first place. I could use a simple `a ++ b` then. I would like to avoid the intermediate collection, as a and b are quite large. – Suma Sep 20 '21 at 10:02
  • 1
    `++` returns a `View[(Int, String)]` which is not a `map` and you cannot do `contains` on it. You can either do `.toMap`, or do contains on each one of the views, or do `collectFirst` on the resulted `View`. I don't see any other option. – Tomer Shetah Sep 20 '21 at 10:47
  • 1
    Something like: `val result = Seq(a, b).exists(_.view.contains(0))` – Tomer Shetah Sep 20 '21 at 10:49
  • From what I can see, `concat` and its `++` aliases on `MapView` return a plain `View` and the `View` does not have the `contains` method. You could concatenate the maps, like mentioned before, but you want to avoid the extra object. If `contains` is the only operation you want, you can get away with `a.contains(x) || b.contains(x)`. – Nikos Paraskevopoulos Sep 20 '21 at 12:51
  • Why ++ returns a View, not Mapview? The implementation of `MapOps.concat` does `mapFactory.from` on the concatenated result, for which ScalaDoc says "collection of type Map generated from given iterable object" – Suma Sep 20 '21 at 12:52
  • "If contains is the only operation you want," no, it is not, I have chosen it more for illustration purposes, but I guess other operations could be implemented in a similar manner. I just hoped `MapVied` will implement this for me. – Suma Sep 20 '21 at 12:54
  • *Why ++ returns a View, not Mapview?* -> I think because you are doing `++` on the `MapView` [ref](https://www.scala-lang.org/api/current/scala/collection/MapView.html#concat[B%3E:A](suffix:scala.collection.IterableOnce[B]):CC[B]), not the `MapOps`. – Nikos Paraskevopoulos Sep 20 '21 at 14:24
  • 3
    See https://github.com/scala/scala/pull/8082 for some background. – Jasper-M Sep 20 '21 at 14:56

2 Answers2

2

Based on Remove concat, ++ and + overloads from MapView it was removed by design, but perhaps you could still provide your own extension method that mimics previous implementation, something like so

implicit class ConcatMapView[K, +V](left: MapView[K, V]) {
  def +++[V1 >: V](right: MapView[K, V1]): MapView[K, V1] =
    new AbstractMapView[K, V1] {
      def get(key: K): Option[V1] = right.get(key) match {
        case s @ Some(_) => s
        case _           => left.get(key)
      }
      def iterator: Iterator[(K, V1)] = left.iterator
        .filter { case (k, _) => !right.contains(k) }
        .concat(right.iterator)
    }
}

val c = a.view +++ b.view // : MapView[Int, String] = MapView((0, "0"), (1, "1"))
c.contains(0) // : Boolean = true
Mario Galic
  • 47,285
  • 6
  • 56
  • 98
0

I don't know the intent behind concat not returning a MapView but you can achieve your goal like this:

val a = Map(0 -> "0")
val b = Map(1 -> "1")

val c = a.view ++ b.view

val contains = c.exists((k,v) => k == 0)
Taha
  • 531
  • 4
  • 21
  • 1
    "c.exists" is O(N), which is a problem for large collection - and for small collections there would hardly be a compelling reason to use view at all. – Suma Sep 20 '21 at 12:56