scala

multiple pattern matching on iteration


val subjectpairs = IndexedSeq((8,0),(3,4),(0,9),(6,1))
val priors =  subjectpairs.map { case( f,_) => f}.filter{ _ >0.0}
val posts =  subjectpairs.map { case( _,l) => l}.filter{ _ >0.0}

This creates 2 sequences from the first , with the first and second members of tuples as non-zero respectively.
However , this needs 2 iterations of the source sequence - which is quite inefficient.

Any suggestion on achieving this with a single iteration ( + any other improvement) - given that the sequence itself will be non-trivial.


Solution

  • Just use mutable ListBuffers. As long as they don't escape the scope of your helper function, there is nothing wrong with using fast mutable buffers and good ol' for-loops and if-elses.

    import scala.collection.mutable.ListBuffer 
     
    def unzipAndFilter[A, B](input: List[(A, B)])(pA: A => Boolean)(pB: B => Boolean): (List[A], List[B]) = {
      val aBldr = new ListBuffer[A]
      val bBldr = new ListBuffer[B]
      for (a, b) <- input do
        if (pA(a)) aBldr += a
        if (pB(b)) bBldr += b
      (aBldr.result, bBldr.result)
    }
    

    Usage:

    unzipAndFilter(List((8,0),(3,4),(0,9),(6,1)))(_ > 0)(_ > 0)
    
    // (List(8, 3, 6),List(4, 9, 1))
    

    Or with Scala 3 extension syntax:

    extension [A, B](input: List[(A, B)])
      def unzipAndFilter[A, B](pA: A => Boolean)(pB: B => Boolean): (List[A], List[B]) = {
        val aBldr = new ListBuffer[A]
        val bBldr = new ListBuffer[B]
        for (a, b) <- input do
          if (pA(a)) aBldr += a
          if (pB(b)) bBldr += b
        (aBldr.result, bBldr.result)
      }
    

    and then

    input.unzipAndFilter(_ > 0)(_ > 0)
    

    There seems to be no unzipCollect in the standard library, unfortunately.