escargot agile
escargot agile

Reputation: 22379

Typecheck when mapping some items in a sequence to None

The following code traverses some list of nodes and creates Paragraph or Heading objects for some of the nodes:

  abstract class Element    
  case class Paragraph(node: Node) extends Element
  case class Heading(mainNode: Node, textNode: Node) extends Element

  val elements =
      parent.child.map(n =>
        if (n.label == "p") Paragraph(n)
        else if (n.label.matches("h\d")) Heading(n, n.child.head)
        else None)

Next, I would like to get rid of the None elements and pass elements to some function that requires Seq[Element]. However, the type of elements is Seq[Product with Serializable] rather than Seq[Element]. Why, and how can I make the type stronger?

Upvotes: 2

Views: 45

Answers (2)

Ben Reich
Ben Reich

Reputation: 16324

I agree with m-z that collect is a great option here. You might sometimes also choose to use flatMap, and return Some for the values you want, and None for the others. Since Option is implicitly converted to an iterable, flatMap will flatten the list to what you want:

val elements = parent.child.flatMap(n =>
    if (n.label == "p") Some(Paragraph(n))
    else if (n.label.matches("h\d")) Some(Heading(n, n.child.head))
    else None)

Upvotes: 2

Michael Zajac
Michael Zajac

Reputation: 55569

Use collect instead of map to keep only the elements you want:

val elements = parent.child.collect {
    case n if (n.label == "p") => Paragraph(n)
    case n if (n.label.matches("h\d")) => Heading(n, n.child.head))
}

Anything that isn't defined within the PartialFunction you pass to collect is discarded.

There's no reason to map to None if you're just going to discard the elements anyway. And if you do for some reason want to keep the None instances, then the others should be mapped to Some[Element] so that you'd have Seq[Option[Element].

Upvotes: 4

Related Questions