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


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

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)))

это было бы лучше, если было!--4--> вариант, который позволит мне работать напрямую с индексами ar1 вместо того, чтобы сначала построить целочисленный список.

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

4 52

4 ответа:

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

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

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

если у вас есть что-то более сложное (например, вам нужно чтобы сделать математику по индексу), каноническим решением является 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)) } }

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

вот как вы зацикливаетесь с индексом в идиоматическом 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

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

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

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")