Please help rewrite loops in Scala way

Hi there.

I’m newbie to Scala.

I wrote such for-loops:

var a = 0
var b = 0
for (x <- 0 until 100) {
  for (y <- 0 until 100) {
    if (arr(x)(y) == z) {
      a = x
      b = y
    }
  }
}

How to rewrite loops in Scala way?

Hi @Xapadi and welcome to the community!

It seems you want the last pair of indexes where the value of a matrix is equal to something, but only looking in the first 100 indexes.

Assuming all that is correct and that you are sure your matrix has a size at least equal to 100 x 100 you may do this:

def lastIndexOfOcurrence[A](m: ArraySeq[ArraySeq[A]])(elem: A): Option[(Int, Int)] =
  Iterator
    .range(start = 99, end = -1, step = -1)
    .map { i =>
      val j = m(i).lastIndexWhere(_ == elem)
      if (j == -1) None
      else Some(i -> j)
    } collectFirst {
      case Some((i, j)) => i -> j
    }

Finally, remember the Scaladoc is your friend.

I think it is fine to use for loops in Scala when they improve code readability. However, you should avoid vars if you can. To reproduce exactly the result you get from your loop without vars you could write:

    val (a, b) = (
      for (x <- 0 until 100;
           y <- 0 until 100 if arr(x)(y) == z) yield (x, y))
      .lastOption
      .getOrElse((0, 0))

That assumes that you want to have a=0 and b=0 when arr has no element z. If you would like to know if z was found you can stop at lastOption.

Without for loops, an alternate to what @BalmungSan suggested:

    val (a, b) =
      arr.zipWithIndex
         .map { case (ax, x) => (x, ax.lastIndexOf(z)) }
         .findLast { case (x, y) => y >= 0 }
         .getOrElse((0, 0))
2 Likes

Note, those are NOT for loops, those are for comprehensions.

Also, in general, it is better to delay the getOrElse outside of your function, that way the caller is free to pick the best default or to compose with other options; of course, there are exceptions.
Finally, I think it would be more readable to split that in multiple statements; like:

def lastIndexOfOccurrence[A](m: ArraySeq[ArraySeq[A]])(elem: A): Option[(Int, Int)] = {
  val indexesOfOccurrence = for {
    x <- 0 until 100
    y <- 0 until 100
    if m(x)(y) == z
  } yield (x, y)
 
  indexesOfOccurrence.lastOption
}
3 Likes

Thank you all very much.