Efficient iteration with index in Scala

Since Scala does not have an old Java for style with an index,

 // does not work val xs = Array("first", "second", "third") for (i=0; i<xs.length; i++) { println("String #" + i + " is " + xs(i)) } 

How can we efficiently iterate without using var ?

You can do it

 val xs = Array("first", "second", "third") val indexed = xs zipWithIndex for (x <- indexed) println("String #" + x._2 + " is " + x._1) 

but the list goes twice - not very efficient.

+61
iteration scala
Jul 26 '11 at 16:39
source share
12 answers

Much worse than crossing twice, it creates an intermediate array of pairs. You can use view . When you execute collection.view , you can think of subsequent calls as lazy during iteration. If you want to return a full, fully implemented collection, you call force at the end. Here it would be useless and expensive. So change your code to

 for((x,i) <- xs.view.zipWithIndex) println("String #" + i + " is " + x) 
+98
Jul 26 '11 at 16:51
source share

It was mentioned that Scala has syntax for for loops:

 for (i <- 0 until xs.length) ... 

or simply

 for (i <- xs.indices) ... 

However, you also asked for efficiency. It turns out Scala for syntax is actually syntactic sugar for higher order methods like map , foreach , etc. Thus, in some cases, these loops can be inefficient, for example. How to optimize for-concepts and loops in Scala?

(The good news is that the Scala team is working to improve this. Here's the problem in the error tracker: https://issues.scala-lang.org/browse/SI-4633 )

For maximum efficiency, you can use the while or, if you insist on removing the use of var , tail recursion:

 import scala.annotation.tailrec @tailrec def printArray(i: Int, xs: Array[String]) { if (i < xs.length) { println("String #" + i + " is " + xs(i)) printArray(i+1, xs) } } printArray(0, Array("first", "second", "third")) 

Note that the optional @tailrec annotation @tailrec useful to ensure that the method is actually tail recursive. The Scala compiler translates tail recursive calls into byte code equivalent to while loops.

+59
Jul 26 '11 at 18:11
source share

Another way:

 scala> val xs = Array("first", "second", "third") xs: Array[java.lang.String] = Array(first, second, third) scala> for (i <- xs.indices) | println(i + ": " + xs(i)) 0: first 1: second 2: third 
+17
Jul 26 2018-11-18T00:
source share

Actually, scala has old Java style loops with an index:

 scala> val xs = Array("first","second","third") xs: Array[java.lang.String] = Array(first, second, third) scala> for (i <- 0 until xs.length) | println("String # " + i + " is "+ xs(i)) String # 0 is first String # 1 is second String # 2 is third 

Where 0 until xs.length or 0.until(xs.length) is the RichInt method, which returns a Range suitable for the loop.

Alternatively, you can try the loop with to :

 scala> for (i <- 0 to xs.length-1) | println("String # " + i + " is "+ xs(i)) String # 0 is first String # 1 is second String # 2 is third 
+11
Jul 26 '11 at 16:44
source share

How about this?

 val a = Array("One", "Two", "Three") a.foldLeft(0) ((i, x) => {println(i + ": " + x); i + 1;} ) 

Output:

 0: One 1: Two 2: Three 
+5
May 30 '13 at 19:37
source share

There is nothing in stdlib that will do this for you without creating garbage, but it's not that hard to write. Unfortunately, I never bothered to figure out how to do proper CanBuildFrom implicit violence in order to make such things generic in the type of collection they are applied to, but if possible, I'm sure someone will enlighten us. :)

 def foreachWithIndex[A](as: Traversable[A])(f: (Int,A) => Unit) { var i = 0 for (a <- as) { f(i, a) i += 1 } } def mapWithIndex[A,B](in: List[A])(f: (Int,A) => B): List[B] = { def mapWithIndex0(in: List[A], gotSoFar: List[B], i: Int): List[B] = { in match { case Nil => gotSoFar.reverse case one :: more => mapWithIndex0(more, f(i, one) :: gotSoFar, i+1) } } mapWithIndex0(in, Nil, 0) } // Tests.... @Test def testForeachWithIndex() { var out = List[Int]() ScalaUtils.foreachWithIndex(List(1,2,3,4)) { (i, num) => out :+= i * num } assertEquals(List(0,2,6,12),out) } @Test def testMapWithIndex() { val out = ScalaUtils.mapWithIndex(List(4,3,2,1)) { (i, num) => i * num } assertEquals(List(0,3,4,3),out) } 
+3
Jul 26 '11 at 17:52
source share

A few more ways to repeat:

 scala> xs.foreach (println) first second third 

foreach and a similar map that will return something (the results of a function that is for println, Unit, so a list of units)

 scala> val lens = for (x <- xs) yield (x.length) lens: Array[Int] = Array(5, 6, 5) 

work with elements, not index

 scala> ("" /: xs) (_ + _) res21: java.lang.String = firstsecondthird 

folding

 for(int i=0, j=0; i+j<100; i+=j*2, j+=i+2) {...} 

can be done with recursion:

 def ijIter (i: Int = 0, j: Int = 0, carry: Int = 0) : Int = if (i + j >= 100) carry else ijIter (i+2*j, j+i+2, carry / 3 + 2 * i - 4 * j + 10) 

The carrier part is just an example to do something with me and j. It should not be Int.

for simpler things, closer to the usual for loops:

 scala> (1 until 4) res43: scala.collection.immutable.Range with scala.collection.immutable.Range.ByOne = Range(1, 2, 3) scala> (0 to 8 by 2) res44: scala.collection.immutable.Range = Range(0, 2, 4, 6, 8) scala> (26 to 13 by -3) res45: scala.collection.immutable.Range = Range(26, 23, 20, 17, 14) 

or without warrant:

 List (1, 3, 2, 5, 9, 7).foreach (print) 
+3
Jul 26 '11 at 22:37
source share

Indeed, calling zipWithIndex in a collection will cross it, as well as create a new collection for couples. To avoid this, you can simply call zipWithIndex in an iterator for the collection. This will simply return a new iterator that tracks the index during the iteration, therefore without creating an extra collection or extra moving.

This is how scala.collection.Iterator.zipWithIndex is currently implemented in 2.10.3:

  def zipWithIndex: Iterator[(A, Int)] = new AbstractIterator[(A, Int)] { var idx = 0 def hasNext = self.hasNext def next = { val ret = (self.next, idx) idx += 1 ret } } 

This should be even a little more efficient than creating a view in a collection.

+3
Nov 14 '13 at 21:17
source share

A simple and efficient way based on the implementation of transform in SeqLike.scala

  var i = 0 xs foreach { el => println("String #" + i + " is " + xs(i)) i += 1 } 
+2
Dec 20 '11 at 18:52
source share

The scala loop is pretty simple. Create any array of your choice.

 val myArray = new Array[String](3) myArray(0)="0"; myArray(1)="1"; myArray(2)="2"; 

Types of cycles

 for(data <- myArray)println(data) for (i <- 0 until myArray.size) println(i + ": " + myArray(i)) 
+2
Jul 01 '13 at 10:16
source share

I have the following approaches

 object HelloV2 { def main(args: Array[String]) { //Efficient iteration with index in Scala //Approach #1 var msg = ""; for (i <- args.indices) { msg+=(args(i)); } var msg1=""; //Approach #2 for (i <- 0 until args.length) { msg1 += (args(i)); } //Approach #3 var msg3="" args.foreach{ arg => msg3 += (arg) } println("msg= " + msg); println("msg1= " + msg1); println("msg3= " + msg3); } } 
+1
Mar 16 '14 at 6:16
source share

The proposed solutions are related to the fact that they either explicitly iterate over the collection or assemble it into a function. It’s natural to stick with the usual Scala idioms and put the index in the usual map or foreach methods. This can be done using memoizing. The resulting code may look like

 myIterable map (doIndexed(someFunction)) 

Here is a way to achieve this. Consider the following utility:

 object TraversableUtil { class IndexMemoizingFunction[A, B](f: (Int, A) => B) extends Function1[A, B] { private var index = 0 override def apply(a: A): B = { val ret = f(index, a) index += 1 ret } } def doIndexed[A, B](f: (Int, A) => B): A => B = { new IndexMemoizingFunction(f) } } 

That is all you need. You can apply this, for example, as follows:

 import TraversableUtil._ List('a','b','c').map(doIndexed((i, char) => char + i)) 

leading to a list

 List(97, 99, 101) 

This way you can use regular Traversable functions by porting your efficient function. Enjoy it!

0
May 08 '12 at 15:53
source share



All Articles