0

How can the following Scala function be refactored to use idiomatic best practices?

  def getFilteredList(ids: Seq[Int], 
      idsMustBeInThisListIfItExists: Option[Seq[Int]], 
      idsMustAlsoBeInThisListIfItExists: Option[Seq[Int]]): Seq[Int] = {

    var output = ids

    if (idsMustBeInThisListIfItExists.isDefined) {
      output = output.intersect(idsMustBeInThisListIfItExists.get)
    }
    if (idsMustAlsoBeInThisListIfItExists.isDefined) {
      output = output.intersect(idsMustAlsoBeInThisListIfItExists.get)
    }

    output 
  }

Expected IO:

val ids = Seq(1,2,3,4,5)
val output1 = getFilteredList(ids, None, Some(Seq(3,5))) // 3, 5
val output2 = getFilteredList(ids, None, None) // 1,2,3,4,5
val output3 = getFilteredList(ids, Some(Seq(1,2)), None) // 1,2
val output4 = getFilteredList(ids, Some(Seq(1)), Some(Seq(5))) // 1,5

Thank you for your time.

newToScala
  • 397
  • 4
  • 16

3 Answers3

2

Here's a simple way to do this:

  implicit class SeqAugmenter[T](val seq: Seq[T]) extends AnyVal {
    def intersect(opt: Option[Seq[T]]): Seq[T] = {
      opt.fold(seq)(seq intersect _)
    }
  }

  def getFilteredList(ids: Seq[Int],
    idsMustBeInThisListIfItExists: Option[Seq[Int]],
    idsMustAlsoBeInThisListIfItExists: Option[Seq[Int]]
  ): Seq[Int] = {
    ids intersect
      idsMustBeInThisListIfItExists intersect 
      idsMustAlsoBeInThisListIfItExists
  }
flavian
  • 28,161
  • 11
  • 65
  • 105
  • Thanks for taking the time to respond with an idiomatic code sample. My thinking is that this solution is ironic in that it falls back to Java's legacy of anonymous (or 'implicit') classes to solve problems. – newToScala Aug 06 '16 at 22:17
  • 1
    This has nothing to do with anonymous classes, I think you are mistaking the concepts. This is simply to do with making the code more fluent, that's all. http://alvinalexander.com/scala/scala-2.10-implicit-class-example – flavian Aug 06 '16 at 22:18
  • There is a fairly simple control structure to achieve the expected output; the scala way is to avoid #isDefined at all costs, which is why I asked the Q, and it is ironic that the idiomatic solution is to resort back to classes when avoiding classes is a reason for using Scala over Java. – newToScala Aug 06 '16 at 22:25
  • @newToScala Ok fair, but the real trick is the `implicit` and using `AnyVal` to avoid the boxing, so there's quite a bit going on that Java couldn't do, the class bit is definitely part of the mix but the least fun bit. Some detail here: http://stackoverflow.com/questions/14929422/should-implicit-classes-always-extend-anyval – flavian Aug 06 '16 at 22:51
2

Yet another way without for comprehensions and implicits:

def getFilteredList(ids: Seq[Int],
                  idsMustBeInThisListIfItExists: Option[Seq[Int]],
                  idsMustAlsoBeInThisListIfItExists: Option[Seq[Int]]): Seq[Int] = {

  val output1 = ids.intersect(idsMustBeInThisListIfItExists.getOrElse(ids))
  val output2 = output1.intersect(idsMustAlsoBeInThisListIfItExists.getOrElse(output1))

  output2
}
satendra
  • 66
  • 6
1

Another similar way, without implicits.

def getFilteredList[A](ids: Seq[A],
                       idsMustBeInThisListIfItExists: Option[Seq[A]],
                       idsMustAlsoBeInThisListIfItExists: Option[Seq[A]]): Seq[A] = {
  val  a = intersect(Some(ids), idsMustBeInThisListIfItExists)(ids)
  val  b = intersect(Some(a), idsMustAlsoBeInThisListIfItExists)(a)
  b
}

def intersect[A](ma: Option[Seq[A]], mb: Option[Seq[A]])(default: Seq[A]) = {
  (for {
     a <- ma
     b <- mb
   } yield {
     a.intersect(b)
   }).getOrElse(default)
}
nishnet2002
  • 268
  • 1
  • 2
  • 9