Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 284
Joernsn Avatar asked Feb 27 '13 21:02

Joernsn


2 Answers

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.

like image 103
om-nom-nom Avatar answered Nov 19 '22 23:11

om-nom-nom


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.

like image 11
12 revsuser166390 Avatar answered Nov 19 '22 23:11

12 revsuser166390