Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing an iterator for a 2D array

I am trying to write an iterator for a 2D array. The following is what I have come up with.

  def rowsTest() {
    val array = Array(
      Array(9, 11, 4, 89),
      Array(7, 62, 34, 2),
      Array(3, 4, 5, 12),
      Array(13, 4, 5, 12),
      Array(3, 24, 5, 12),
      Array(3, 4, 35, 12)
    )
    def rows: Iterator[Iterator[Int]] = {
      new Iterator[Iterator[Int]] {
        private var rowIndex = 0

        def hasNext: Boolean = rowIndex < 6

        def next: Iterator[Int] = {
          val rowIterator = new Iterator[Int] {
            private var columnIndex = 0

            def next: Int = {
              val p = array(columnIndex)(rowIndex)
              columnIndex += 1
              println("ColIndex = "+ columnIndex.toString)
              p
            }

            def hasNext: Boolean = columnIndex < 4
          }
          rowIndex += 1
          println("RowIndex = "+ rowIndex.toString)
          rowIterator
        }
      }
    }
    for(row <- rows; elem <- row)
      println(elem)
  }

The above code when run skips the first row, and also gives an ArrayIndexOutOfBoundsException when all elements have been printed. Can you help me figure out where I've gone wrong?

Thank you,
Siddharth Raina.

like image 536
Siddharth Raina Avatar asked Dec 27 '22 23:12

Siddharth Raina


2 Answers

How about the following code?

val array = Array(Array(1,2,3),Array(4,5,6),Array(7,8,9))
array.view.flatten.iterator

It works, as tested in REPL. Though I don't know if I achieve what I intended with "view". Any comments are welcome.

Edit

I forgot the author wanted a nested iterator.

array.iterator.map(_.iterator)

This certainly works without the "view" and without overhead.

like image 72
ziggystar Avatar answered Jan 12 '23 05:01

ziggystar


I can't tell from your code what you actually want to do.

If you want traverse your array with iterators of iterators, there's already an easy way to do it:

val a2d = Array.tabulate(4,4)((i,j)=>4*i+j)
a2d.iterator.map(_.iterator)

And if you decide you want a single iterator, you can do that too:

a2d.iterator.flatMap(_.iterator)

If you want to traverse columns even though the array is in row-major order, then you have a little more work to do (which I think is what you were trying to do, but you mixed up your array indices, and maybe some other things):

def iterateColumns(aai: Array[Array[Int]]) = new Iterator[Iterator[Int]] {
  private[this] var j = -1
  private[this] val shortest = if (aai.length==0) 0 else aai.map(_.length).min
  def hasNext = j+1 < shortest
  def next = {
    j += 1
    new Iterator[Int] {
      private[this] var i = -1
      def hasNext = i+1 < aai.length
      def next = {
        i += 1
        aai(i)(j)
      }
    }
  }
}

Now you can

scala> for (row <- a2d.iterator.map(_.iterator)) println(row.mkString(" "))
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15

scala> for (col <- iterateColumns(a2d)) println(col.mkString(" "))
0 4 8 12
1 5 9 13
2 6 10 14
3 7 11 15

(You also ought to be able to do a2d.view.transpose.iterator.map(_.iterator) to avoid making a copy, but unfortunately it doesn't look like this works the way you'd hope in 2.8.1.)

like image 38
Rex Kerr Avatar answered Jan 12 '23 06:01

Rex Kerr