Reputation: 1204
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
Reputation: 16412
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)))
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)
}
}
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