igalbenardete
igalbenardete

Reputation: 307

A better implementation for pattern matching a tree-like option structure

  def leadParser(optionTuples: Option[(Option[(Option[(Option[(Option[(
                                Option[Iterable[EmailLead]],
                                Option[Iterable[QuestionLead]])],
                                Option[Iterable[LocalMarketQuestionLead]])],
                                Option[Iterable[OfferLead]])],
                                Option[Iterable[Bid]])],
                                Option[Iterable[CreditApplicationLeadWithEbayId]])]) = {
    optionTuples match{
      case None => (None, None, None, None, None, None)
      case Some(c) =>
        val creditApplictaionIteratorOption = c._2
        c._1 match {
          case None => (None, None, None, None, None, creditApplictaionIteratorOption)
          case Some(b) =>
            val bidIteratorOption = b._2
            b._1 match {
              case None => (None, None, None, None, bidIteratorOption, creditApplictaionIteratorOption)
              case Some(o) =>
                val offerIteratorOption = o._2
                o._1 match {
                  case None => (None, None, None, offerIteratorOption, bidIteratorOption, creditApplictaionIteratorOption)
                  case Some(l) =>
                    val localMarketQuestionIteratorOption = l._2
                    l._1 match {
                      case None => (None, None, localMarketQuestionIteratorOption, offerIteratorOption, bidIteratorOption, creditApplictaionIteratorOption)
                      case Some(q) =>
                        val questionIteratorOption = q._2
                        val emailIteratorOption = q._1
                        (emailIteratorOption, questionIteratorOption, localMarketQuestionIteratorOption, offerIteratorOption, bidIteratorOption, creditApplictaionIteratorOption)
                        }
                    }
                }
            }
        }
}

I know the input Option[... looks insane but this is what I receive from a Spark operation so I have to deal with it. Could there be a better way of getting all the Iterator Options from this complicated tuple/option structure ?

Upvotes: 4

Views: 274

Answers (1)

Ben Reich
Ben Reich

Reputation: 16324

You can use pattern matching to declare variables, not just in case statements. This unlocks several non-nesting possibilities that will be more readable. For example:

def leadParser(optionTuples: Option[(Option[(Option[(Option[(Option[(Option[Iterable[EmailLead]],Option[Iterable[QuestionLead]])],Option[Iterable[LocalMarketQuestionLead]])],Option[Iterable[OfferLead]])],Option[Iterable[Bid]])],Option[Iterable[CreditApplicationLeadWithEbayId]])]) = {
    val (creditRest, credit) = optionTuples.getOrElse(None -> None)
    val (bidRest, bid) = creditRest.getOrElse(None -> None)
    val (offerRest, offer) = bidRest.getOrElse(None -> None)
    val (localRest, local) = offerRest.getOrElse(None -> None)
    val (email, question) = localRest.getOrElse(None -> None)
    (credit, bid, offer, local, email, question)
}

Here we've used getOrElse on each successively nested Option in order to encapsulate the fallback values.

The pattern matching syntax (in both variable declaration and match clauses) can be nested to account for internal structure. That gives you another possibility:

def leadParser(optionTuples: Option[(Option[(Option[(Option[(Option[(Option[Iterable[EmailLead]],Option[Iterable[QuestionLead]])],Option[Iterable[LocalMarketQuestionLead]])],Option[Iterable[OfferLead]])],Option[Iterable[Bid]])],Option[Iterable[CreditApplicationLeadWithEbayId]])]) = {
    optionTuples match {
        case Some((Some((Some((Some((Some((a, b)), c)), d)), e)), f)) => (a, b, c, d, e, f)
        case Some((Some((Some((Some((None, c)), d)), e)), f)) => (None, None, c, d, e, f)
        case Some((Some((Some((None, d)), e)), f)) => (None, None, None, d, e, f)
        case Some((Some((None, e)), f)) => (None, None, None, None, e, f)
        case Some((None, f)) => (None, None, None, None, None, f)
        case None => (None, None, None, None, None, None)
    }
}

Here we use several nested pattern matches to capture the internal structure of the complex value.

The other natural choices here would be some sort of for comprehension that prevents nesting, or a generic recursive function that flattens this type of optional tuple. Finally, if this is too cumbersome, you might consider using a library like Shapeless, might allow you to work with this complex type more concisely. I think that would be overkill, though.

Upvotes: 2

Related Questions