Home > other >  Check sequence String to contain strictly two patterns
Check sequence String to contain strictly two patterns

Time:08-19

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
  • Related