Получить индекс текущего элемента в методе foreach для Traversable?

Предположим, что у меня есть два массива:

val ar1 = Array[String]("1", "2", "3")
val ar2 = Array[String]("1", "2", "3", "4")

Теперь для каждого элемента ar1, я хочу сначала соединить этот элемент с соответствующим элементом ar2, а затем распечатать результат. Один из способов сделать это будет следующим:

List.range(0, ar1.size).foreach(i => println(ar1(i)+ar2(i)))

Было бы лучше, если бы существовал вариант foreach, который позволил бы мне напрямую работать с индексами ar1 вместо того, чтобы сначала создавать целочисленный список.

Возможно, есть лучший способ?

Ответ 1

Один очень удобный способ сделать это с помощью метода zipped для кортежей. Поместите две коллекции в, выведите два аргумента функции!

(ar1,ar2).zipped.foreach((x,y) => println(x+y))

Это удобно писать и быстро, так как вам не нужно создавать кортеж для хранения каждой пары (как и с (ar1 zip ar2)), которую вам нужно снова разделить. Обе формы zip останавливаются при исчерпании более короткой из двух коллекций.

Если у вас есть что-то более сложное (например, вам нужно сделать математику по индексу), каноническое решение - закрепить в индексе:

ar1.zipWithIndex.foreach{ case(x,i) => println(x+ar2(i)) }

Используемый вами метод быстрее и компактно выполняется следующим образом: может быть полезно:

ar1.indices.foreach(i => println(ar1(i)+ar2(i)))

хотя это работает только в том случае, если первая коллекция не больше второй. Вы также можете указать свои диапазоны:

(0 until (ar1.size min ar2.size)).foreach(i => println(ar1(i)+ar2(i)))

чтобы обойти эту проблему. (Вы можете понять, почему zip и zipped являются предпочтительными, если только вы не слишком сложны, чтобы это было легко работать.)

Если это не параллельный набор (и обычно это не так, если вы не вызываете .par), также возможно, хотя и не рекомендуется, отслеживать измененную переменную:

{ var i=-1; ar1.foreach{ x => i += 1; println(x+ar2(i)) } }

Существует очень ограниченное число случаев, когда это необходимо (например, если вы можете пропустить или вернуться к некоторым другим коллекциям); если вы не можете этого сделать, вы, как правило, получите код, который легче рассуждать.

Ответ 2

Вот как вы петляете с индексом в идиоматическом Scala:

scala> List("A", "B", "C").zipWithIndex foreach { case(el, i) =>
     |   println(i + ": " + el)
     | }
0: A
1: B
2: C

И вот идиоматический способ Scala делать то, что вы пытаетесь достичь в своем коде:

scala> val arr1 = Array("1", "2", "3")
arr1: Array[java.lang.String] = Array(1, 2, 3)

scala> val arr2 = Array("1", "2", "3", "4")
arr2: Array[java.lang.String] = Array(1, 2, 3, 4)

scala> (arr1, arr2).zipped.map(_ + _) foreach println
11
22
33

Ответ 3

У меня не было возможности проверить это, но это должно сделать трюк:

ar1.zip(ar2).foreach(x => println(x._1 +x._2))

Ответ 4

zip сделает это:

ar1 zip ar2 foreach { p => println(p._1 + p._2) }

Это даст:

11
22
33

Обратите внимание, что вам не нужен [String] общий тип, будет выведен компилятором:

val ar1 = Array("1", "2", "3")
val ar2 = Array("1", "2", "3", "4")