1

This is a kind of cosmetic Scala question. A list with objects needs to be filtered on the objects' attributes. I need to report if the first check on the attribute results in an empty list. Simplified code:

case class Account (id: Int, balance: Float)

def doFilter(list: List[Account], focusId: Int, thresHold: Float): List[Account] = {
  list.filter(_.id == focusId)
  // ## if at this point if the list is empty, an error log is required.
    .filter(_.balance >= thresHold)
}

var accounts = List(Account(1, 5.0f), Account(2, -1.0f), Account(3, 10f), Account(4, 12f))

println(s"result ${doFilter(accounts, 1, 0f)}")

Of course I can split up the filter statements and check the intermediate result but I was hoping I could do it more scala way.. I thought something like.

list.filter(_.id == focusId) 
match { case List() => { println "error"; List()}
case _ => _}

But that doesn't work. Is there a functional (or fluent) way to implement the desired behaviour?

dr jerry
  • 9,768
  • 24
  • 79
  • 122

3 Answers3

2

If you need it once, then logging an intermediate result is, probably, the simplest way. If you need this at several places, you can make the code a bit nicer using extension methods:

implicit class ListOps[+A](val list: List[A]) extends AnyVal {
    def logIfEmpty(): List[A] = {
      if (list.isEmpty) {
      println("Error: empty list") 
      // or whatever; you can even pass it as an argument
    }
    list
  }
}

Then you can use it like this:

def doFilter(list: List[Account], focusId: Int, thresHold: Float): List[Account] = list
  .filter(_.id == focusId)
  .logIfEmpty()
  .filter(_.balance >= thresHold)
zhelezoglo
  • 212
  • 2
  • 11
2

The following code is a slight modification from this SO answer from Rex Kerr.

implicit class KestrelPattern[A](private val repr: A) extends AnyVal {
  def tee[B](f: A => B) = { f(repr); repr } // B is thrown away (Unit)
}

He called it tap. I chose tee because of the similarity to the unix tee command.

Usage:

scala> List[Int](3,5,7).tee{x => if (x.isEmpty) println("ERROR")}.sum
res42: Int = 15

scala> List[Int]().tee{x => if (x.isEmpty) println("ERROR")}.sum
ERROR
res43: Int = 0
Community
  • 1
  • 1
jwvh
  • 50,871
  • 7
  • 38
  • 64
1

The pattern matching works, the error of your code comes from the fact that you are trying to return _ in the second case, you may want to check here and here for why this could be a problem:

accounts.filter(_.id == 1) match {
       case List() => { println("error"); List() }
       case x => x.filter(_.balance > 1.0)
}
// res19: List[Account] = List(Account(1,5.0))


accounts.filter(_.id == 5) match {
       case List() => { println("error"); List() }
       case x => x.filter(_.balance > 1.0)
}
// error
// res20: List[Account] = List()
Community
  • 1
  • 1
Psidom
  • 209,562
  • 33
  • 339
  • 356