Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Map flatten and flatmap not equivalent

I thought that Scala construct map(f).flatten was equivalent to flatMap(f). But with this example, it is not the case. I wonder what is the role of the case class in it. If I use integers, both are equivalent. But in my case, I cannot.

case class CTest(v: Int)
val s = Set(Map(CTest(0) -> List(0, 3), CTest(1) -> List(0, 2)))
val possibilities = s flatMap { m =>
  val mapping = m flatMap {
    case (label, destNodes) => destNodes map {
      case nodes => (label, nodes) }
  }
  mapping
}
possibilities

Yields

Set((CTest(0),3), (CTest(1), 2))

whereas

case class CTest(v: Int)
val s = Set(Map(CTest(0) -> List(0, 3), CTest(1) -> List(0, 2)))
val possibilities = s flatMap { m =>
  val mapping = m map {
    case (label, destNodes) => destNodes map {
      case nodes => (label, nodes) }
  }
  mapping.flatten
}
possibilities

yields

Set((CTest(0),0), (CTest(0),3), (CTest(1),0), (CTest(1),2))

Any idea why?

like image 851
Mikaël Mayer Avatar asked Nov 26 '13 11:11

Mikaël Mayer


1 Answers

This happens due to intermediate data structures.

I'll take simple version of your example.

val m = Map(CTest(0) -> List(0, 3), CTest(1) -> List(0, 2))

When using flatMap you directly create a Map[CTest, Int]

scala> m flatMap {
 |     case (label, destNodes) => destNodes map {
 |       case nodes => (label, nodes) }
 |   }
res3: scala.collection.immutable.Map[CTest,Int] = Map(CTest(0) -> 3, CTest(1) -> 2)

In here, due to the uniqueness of the keys of Map, (CTest(0), 0) and (CTest(1), 0) will be dropped from the result. when you flatMap it over set, you will get a Set of Tuples which were in the Map.

In your second example, you map and flatten.

val mapping = m map {
 |     case (label, destNodes) => destNodes map {
 |       case nodes => (label, nodes) }
 |   }
mapping: scala.collection.immutable.Iterable[List[(CTest, Int)]] = List(List((CTest(0),0), (CTest(0),3)), List((CTest(1),0), (CTest(1),2)))

mapping.flatten
res4: scala.collection.immutable.Iterable[(CTest, Int)] = List((CTest(0),0), (CTest(0),3), (CTest(1),0), (CTest(1),2))

There isn't any Map or another uniqueness preserved data structure created in the middle of the process. So values are not dropped.

like image 183
tiran Avatar answered Oct 13 '22 05:10

tiran