Question

Is there an existing function that shifts a Range producing another Range, like

val r = 1 to 5
val s = r.map(_ + 2)  // produces Vector(3, 4, 5, 6, 7)

I would like to get 3 to 7.

Was it helpful?

Solution

Here is how I would implement it:

implicit class RangeHasShift(val r: Range) extends AnyVal {
  def shift(n: Int): Range = {
    val start1 = r.start + n
    val end1   = r.end   + n

    // overflow check
    if ((n > 0 && (start1 < r.start || end1 < r.end)) ||
        (n < 0 && (start1 > r.start || end1 > r.end)))
      throw new IllegalArgumentException(s"$r.shift($n) causes number overflow")

    if (r.isInclusive)
      new Range.Inclusive(start1, end1, r.step)
    else
      new Range          (start1, end1, r.step)
  }
}

def check(r: Range) = assert(r == r.shift(123).shift(-123))

check(1 to 10)
check(1 to -1)
check(1 to -1 by -1)
check(1 to 10 by 3)
check(1 until 10)
check(1 until -1)
check(1 until -1 by -1)
check(1 until 10 by 3)

I wonder if this exists somewhere in the API?

OTHER TIPS

If your main goal is not to have all values in memory when shifting the range you could use a View:

scala> (1 to 999999999).view.map(_ + 2)
res0: scala.collection.SeqView[Int,Seq[_]] = SeqViewM(...)

That would resemble the old implementation of Range which returned a lazy sequence.

Another simple approach that returns an Inclusive Range

val newRange = previousRange.start + shift to previousRange.end + shift

or

val newRange = Range.inclusive(previousRange.start + shift, previousRange.end + shift)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top