Joernsn
Joernsn

Reputation: 2349

Yield only if pattern match

I am building a list of different case class objects based on a loop and a pattern match. I want to exclude (skip) the items hitting the default case (essentially filtering the list, and mapping to types in one step).

I'm probably too new to Scala to see all the possibilities. My attempt:

    val events = for (ev <- data ) yield {

        ev.sport match {
            case "FOOTBALL" => new FootballEvent(ev)
            case "SOCCER" => new SoccerEvent(ev)
            case _ => None
        }

    }

I could filter the list afterwards, but I suspect there's some fancy Scala way of doing this :)

Please let me know if you have any idea of how this should best be done!

Upvotes: 14

Views: 13828

Answers (2)

user166390
user166390

Reputation:

The standard filter in for-yield is obtained with x <- y if f(x,..). Here is an example that uses a Partial Function.

val m: PartialFunction[Event, Event] = ev => ev.sport match {
  case "FOOTBALL" => new FootballEvent(ev)
  case "SOCCER" => new SoccerEvent(ev)
};

for { ev <- data if m.isDefindAt(ev)  
      val x = m(ev)
} yield x

// or, without the temporary binding:
for (ev <- data if m.isDefindAt(ev)) yield m(ev)

Note the similarity with Traversable.collect1 mentioned in the other answer, which has this signature def collect[B](pf: PartialFunction[A, B]): CC[B] and returns "a new collection resulting from applying the given partial function pf to each element on which it is defined and collecting the results".

An alternative without if is a variation of bwroga's deleted answer:

for { ev <- data;
      x <- ev.sport match {
        case "FOOTBALL" => Some(new FootballEvent(ev))
        case "SOCCER" => Some(new SoccerEvent(ev))
        case _ => None
      }
} yield x

This filters by subsequently iterating through None (i.e. "0 items") or Some (i.e. "1 item") after the initial map.


1 If someone can tell me how to link to individual methods in the "new" scaladoc, I'd be most grateful.

Upvotes: 11

om-nom-nom
om-nom-nom

Reputation: 62835

It is not that it is yield syntax, but you can use collect with pattern matching:

val events = data.collect { ev => ev.sport match {
     case "FOOTBALL" => new FootballEvent(ev) 
     case "SOCCER"   => new SoccerEvent(ev)
}}

Unlike better known .map and .foreach it wont fail on "other" case and instead just drop unmatched items.

Upvotes: 30

Related Questions