How to extend a Scala list to enable slicing not by explicit position but by given predicate/condition

后端 未结 3 1456
有刺的猬
有刺的猬 2020-11-28 16:41

For

trait Item
case class TypeA(i: Int) extends Item
case class TypeB(i: Int) extends Item

consider a Scala list of items such as

相关标签:
3条回答
  • 2020-11-28 17:30

    Aren't you looking for filter, which works (almost) without any tweaks for your examples?

    $ sbt console
    scala> trait Item
    scala> case class TypeA(i: Int) extends Item
    scala> case class TypeB(i: Int) extends Item
    scala> val myList = List(TypeA(1), TypeB(11), TypeB(12), 
                             TypeA(2), TypeB(21), 
                             TypeA(3), TypeB(31))
    myList: List[Product with Serializable with Item] = List(TypeA(1), TypeB(11), TypeB(12), TypeA(2), TypeB(21), TypeA(3), TypeB(31))
    

    your first works unaltered:

    scala> myList.filter { x => x.isInstanceOf[TypeA] }
    res0: List[Product with Serializable with Item] = List(TypeA(1), TypeA(2), TypeA(3))
    

    your second requires a default case:

    scala> myList.filter { case TypeA(x) => x < 10; case _ => false }
    res2: List[Product with Serializable with Item] = List(TypeA(1(3))
    

    See also collect, which takes a partial function instead of a boolean predicate:

    scala> myList.collect { case z @ TypeA(x) if x < 10 => z }
    res3: List[TypeA] = List(TypeA(1), TypeA(2), TypeA(3))
    

    and can transform as well:

    scala> myList.collect { case TypeA(x) if x < 10 => x }
    res4: List[Int] = List(1, 2, 3)
    
    0 讨论(0)
  • 2020-11-28 17:37

    List already has a slice method - it takes a subset of elements between a start and end index. What you're looking for is repeated application of the span method:

    def span(p: (A) ⇒ Boolean): (List[A], List[A])
    

    Which is documented as:

    Splits this list into a prefix/suffix pair according to a predicate.

    Note: c span p is equivalent to (but possibly more efficient than) (c takeWhile p, c dropWhile p), provided the evaluation of the predicate p does not cause any side-effects.

    returns: a pair consisting of the longest prefix of this list whose elements all satisfy p, and the rest of this list.

    You can get what you need by repeatedly using this method with an inverse predicate, and an extra bit of logic to ensure that none of the returned Lists are empty.

    import annotation.tailrec
    
    def multiSpan[A](xs: List[A])(splitOn: (A) => Boolean): List[List[A]] = {
      @tailrec
      def loop(xs: List[A], acc: List[List[A]]) : List[List[A]] = xs match {
        case Nil => acc
    
        case x :: Nil => List(x) :: acc
    
        case h :: t =>
          val (pre,post) = t.span(!splitOn(_))
          loop(post, (h :: pre) :: acc)
      }
      loop(xs, Nil).reverse
    }
    

    UPDATE

    As requested in comments on the original post, here's a version that enriches list instead of being a standalone method:

    implicit class AddMultispanToList[A](val list: List[A]) extends AnyVal {
      def multiSpan(splitOn: (A) => Boolean): List[List[A]] = {
        @tailrec
        def loop(xs: List[A], acc: List[List[A]]) : List[List[A]] = xs match {
          case Nil => acc
    
          case x :: Nil => List(x) :: acc
    
          case h :: t =>
            val (pre,post) = t.span(!splitOn(_))
            loop(post, (h :: pre) :: acc)
        }
        loop(list, Nil).reverse
      }
    }
    

    Use as:

    myList.multiSpan(_.isInstanceOf[TypeA])
    
    0 讨论(0)
  • 2020-11-28 17:38

    Why couldn't you use partition method from the standard API?

    example:

    scala> val l = List(3,5,4,6)
    l: List[Int] = List(3, 5, 4, 6)
    
    scala> 
    
    scala> val (odd,even) = l.partition(_ %2 ==1)
    odd: List[Int] = List(3, 5)
    even: List[Int] = List(4, 6)
    

    For your example:

    scala> val (typeA,typeB) = myList.partition(_.isInstanceOf[TypeA])
    typeA: List[Product with Serializable with Item] = List(TypeA(1), TypeA(2), TypeA(3))
    typeB: List[Product with Serializable with Item] = List(TypeB(11), TypeB(12), TypeB(21), TypeB(31))
    
    0 讨论(0)
提交回复
热议问题