user1553248
user1553248

Reputation: 1204

Removing value from range

I have the following case class:

case class objRanges(rangeVals:List[rangeNums])

where rangeNums represents a range of values and is also a case class:

case class rangeNums(lowValue:Int, highValue:Int)

I would like to implement a subtraction method for objRanges, one with the following signature:

def -(numberRemove:Int): objRanges = {

The number would then be removed from all the ranges in the list rangeVals, splitting rangeNums that contain the number into two rangeNums (one from lowValue to n-1 and another from n+1 to highValue). Would this be easier to implement using a for comprehension or foldLeft?

For example, rangeVals currently holds the elements:

[(1,3),(5,10)]

Calling the - method with 7 as a parameter should return a new instance of objRanges with the elements:

[(1,3),(5,6),(8,10)]

While subtracting an element which is the lower bound of a range should just increase the lower bound by one, so subtracting 5 in the rangeVals should return:

[(1,3),(6,10)]

The same applies for the upper bound. If rangeVals currently holds:

[(1,3),(5,7)]

Then removing 7 should return

[(1,3),(5,6)]

The last boundary case would be where there is only one number in a range, and that number is to be deleted. For example, if RangeVals currently holds:

[(1,3),(7,7)]

Removing 7 should just remove the entire range, returning a new instance with rangeVals equal to:

[(1,3)]

And how would the solution change if I was looking instead to remove all numbers in the ranges less than numberRemove?

Upvotes: 0

Views: 169

Answers (1)

yǝsʞǝla
yǝsʞǝla

Reputation: 16412

  1. Using map:

    case class RangeNums(lowValue: Int, highValue: Int)
    
    case class ObjRanges(rangeVals: List[RangeNums]) {
      def -(n: Int): ObjRanges = {
        val res = rangeVals.map {
          case e @ RangeNums(l, h) =>
            if (n > l && n < h) RangeNums(l, n - 1) :: RangeNums(n + 1, h) :: Nil
            else if (n == l) RangeNums(l + 1, h) :: Nil
            else if (n == h) Nil
            else e :: Nil
        }.flatten
        ObjRanges(res)
      }
    }
    

    test with REPL:

    scala> val l = RangeNums(0, 10) :: RangeNums(1, 3) :: Nil
    l: List[RangeNums] = List(RangeNums(0,10), RangeNums(1,3))
    
    scala>  val r = ObjRanges(l)
    r: ObjRanges = ObjRanges(List(RangeNums(0,10), RangeNums(1,3)))
    
    scala> r - 5
    res0: ObjRanges = ObjRanges(List(RangeNums(0,4), RangeNums(6,10), RangeNums(1,3)))
    
    scala> r - 2
    res1: ObjRanges = ObjRanges(List(RangeNums(0,1), RangeNums(3,10), RangeNums(1,1), RangeNums(3,3)))
    
    scala> r - 10
    res2: ObjRanges = ObjRanges(List(RangeNums(1,3)))
    
    scala> r - 0
    res3: ObjRanges = ObjRanges(List(RangeNums(1,10), RangeNums(1,3)))
    
  2. A tailrec solution:

    import scala.annotation.tailrec
    
    case class ObjRanges(rangeVals: List[RangeNums]) {
      def -(n: Int): ObjRanges = {
        @tailrec
        def minRec(n: Int, rem: List[RangeNums], accu: List[RangeNums]): List[RangeNums] =
          rem match {
            case (e @ RangeNums(l, h)) :: t =>
              val tmpRes =
                if (n > l && n < h) RangeNums(n + 1, h) :: RangeNums(l, n - 1) :: accu
                else if (n == l) RangeNums(l + 1, h) :: accu
                else if (n == h) accu
                else e :: accu
              minRec(n, t, tmpRes)
            case Nil =>
              accu
          }
        ObjRanges(minRec(n, rangeVals, Nil).reverse)
      }
    }
    
  3. Using fold:

    case class ObjRanges(rangeVals: List[RangeNums]) {
       def -(n: Int): ObjRanges = {
         def minF(accu: List[RangeNums], e: RangeNums) = {
           val l = e.lowValue
           val h = e.highValue
           if (n > l && n < h) RangeNums(n + 1, h) :: RangeNums(l, n - 1) :: accu
           else if (n == l) RangeNums(l + 1, h) :: accu
           else if (n == h) accu
           else e :: accu
         }
         val res = (List[RangeNums]() /: rangeVals)((accu, e) => minF(accu, e))
         ObjRanges(res.reverse)
       }
    }
    

Upvotes: 2

Related Questions