I have a list of String where the String element could start with prefix of AA, BB or CC. How can I check if the list must and only contains String startWith both AA and BB but not CC. This is what I have now which is working, is that a better way to do it? Thanks.
private final val ValidPatternA: String = "^AA.*"
private final val ValidPatternB: String = "^BB.*"
private final val InvalidPatternC: String = "^CC.*"
def main(args: Array[String]): Unit = {
println(isValid(Seq())) // false
println(isValid(Seq("AA0"))) // false
println(isValid(Seq("BB1"))) // false
println(isValid(Seq("CC2"))) // false
println(isValid(Seq("AA0", "BB1", "CC2"))) // false
println(isValid(Seq("AA0", "CC2"))) // false
println(isValid(Seq("BB1", "CC2"))) // false
println(isValid(Seq("AA0", "BB1"))) // true
}
private def isValid(listOfString: Seq[String]) =
!listOfString.exists(_.matches(InvalidPatternC)) &&
listOfString.exists(_.matches(ValidPatternA)) &&
listOfString.exists(_.matches(ValidPatternB))
CodePudding user response:
The code you have is clear and expressive, so the only concern can be performance. A recursive function can do one pass efficiently:
def isValid(listOfString: Seq[String]) = {
@annotation.tailrec
def loop(rem: List[String], foundA: Boolean, foundB: Boolean): Boolean =
rem match {
case Nil => foundA && foundB
case s"CC$_" :: _ => false
case s"AA$_" :: tail => loop(tail, true, foundB)
case s"BB$_" :: tail => loop(tail, foundA, true)
case hd :: tail => loop(tail, foundA, foundB)
}
loop(listOfString.toList, false, false)
}
The @annotation.tailrec
indicates that this will be compiled into a fast loop with rem
, foundA
and foundB
stored in local variables, and loop
being a goto back to the start of the function.
CodePudding user response:
You can optimize it by using bit mask to save on number of collection traversals.
private def isValid(listOfString: Seq[String]) =
listOfString.foldLeft(0) { (mask, str) =>
mask | str.matches(ValidPatternA).compare(false) | str.matches(ValidPatternB).compare(false) << 1 | str.matches(InvalidPatternC).compare(false) << 2
} == 3 // 1 & 2 & ^4