Domanda

I have a map and I want to transform it, via yield, to a new collection, filtering based on the keys. I only want a subset of the map entries to be in the new collection.

scala> val the_map = Map(1->10, 2->41, 3->41, 27->614, 400->541, 5214 -> 2)
the_map: scala.collection.immutable.Map[Int,Int] = Map(1 -> 10, 2 -> 41, 27 -> 614, 3 -> 41, 400 -> 541)

scala> val transformed = for ((k, v) <- the_map) yield {if (k < 10) { v * 10 } else if (k > 100 && k < 400) { v * 5 }}
transformed: scala.collection.immutable.Iterable[AnyVal] = List(100, 410, (), 410, 2705, ())

So I basically want that, but without the ()s in there, and the type to be Iterable[Int]. What's the right approach here? I could filter to remove the ()s and then cast, but that seems wrong. I could Some all the values and put a None at the bottom, and then call flatten, but that seems excessive. Is there a clean way to go? I just want yield to ignore the ()'s that are returned when no if statement matches.

È stato utile?

Soluzione

flatMap is the way to go here:

val transformed =
  the_map.flatMap {
    case (k, v) =>
      if (k < 10)
        Some(v * 10)
      else if (k > 100 && k < 400)
        Some(v * 5)
      else None
  }

You could also do it explicitly with a fold (though I find it less clear than the flatMap approach):

val transformed2 =
  the_map.foldLeft(Vector.empty[Int]) {
    case (z, (k, v)) =>
      if (k < 10)
        z :+ (v * 10)
      else if (k > 100 && k < 400)
        z :+ (v * 5)
      else z
  }

Altri suggerimenti

collect takes a partial function and only returns values where the partial function is defined.

val theMap = Map(1->10, 2->41, 3->41, 27->614, 400->541, 5214->2)

val res = theMap.collect({
  case (k, v) if k < 10 => v * 10
  case (k, v) if k > 100 && k < 400 => v * 5
})

res // List(100, 410, 410)

First, I'm wondering where you get your 2705 from... if I paste your code, my result is:

transformed: scala.collection.immutable.Iterable[AnyVal] = List((), 100, 410, (), 410, ())

not

transformed: scala.collection.immutable.Iterable[AnyVal] = List(100, 410, (), 410, 2705, ())

dhg's answer is probably best in this case, since a repeated condition in the for comprehension and the yield can be trickier to read. It can be expressed this way if you choose though, like so:

val transformed = 
    for( (k,v) <- the_map if k < 10 || (k > 100 && k < 400) ) 
    yield if (k < 10) v*10 else v*5
transformed: scala.collection.immutable.Iterable[Int] = List(100, 410, 410)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top