Reputation: 164
I have a requirement where I have to search a character in vector of vectors. I have written a method which is very crude. What is a better way to search an element in vector of vectors.
Here is my code:
def search( xs: Vector[Vector[Char]], char: Char, rowIndex: Int): Pos = xs.headOption match {
case None => Pos(-1, -1)
case Some(row) => {
val tuple = searchHelper(row, char, 0)
if(tuple._1)
Pos(rowIndex, tuple._2)
else
search(xs.tail, char, rowIndex +1)
}
}
def searchHelper(xs: Vector[Char], char: Char, colIndex: Int): (Boolean, Int) = xs.headOption match {
case None => (false, colIndex)
case Some(col) =>
if(col == char)
(true, colIndex)
else
searchHelper(xs.tail, char, colIndex +1)
}
search(vector, c, 0)
Here is the input:
val input =
"""ooo-------
|oSoooo----
|ooooooooo-
|-ooooooooo
|-----ooToo
|------ooo-""".stripMargin
val vector =
Vector(input.split("\n").map(str => Vector(str: _*)): _*)
val c = 'S'
Upvotes: 0
Views: 624
Reputation: 417
Simplest way of doing it :
def findChar(c: Char, levelVector: Vector[Vector[Char]]): Pos =
val x = levelVector.indexWhere(_.contains(c))
val y = levelVector(x).indexWhere(_ == c)
Pos(x,y)
Indeed you have to assume there is only one correct answer. Main idea :
Upvotes: 0
Reputation: 40500
Something like this, perhaps?
xs
.iterator
.zipWithIndex
.map { case (chars, idx) => (idx, chars.indexOf(c)) }
.collectFirst {
case (outer, inner) if(inner >= 0) => Pos(outer, inner)
}.getOrElse(Pos(-1, 1))
To address a concern from the comments: this is not iterating through the entire list multiple times (not even once completely, as long as it finds a match earlier). Scala iterators are wonderful that way: you can write a chain of calls, that conceptually looks like a bunch of sequential transformations to the entire list, but is really executed lazily, element by element: the first element is is extracted from list, converted to a tuple (chars, 0)
, fed to the map
, then the result of that is sent to collectFirst
, if the condition matches there, it stops and returns right away, otherwise, next element is taken from list, made into a tuple (chars, 1)
, fed to the .map
, etc ...
Upvotes: 3
Reputation: 2173
If, with better, you mean more concise, you could try leveraging the methods on Scalas collections:
def search( xs: Vector[Vector[Char]], c: Char ): (Int, Int) = {
var innerIndex = -1
val outerIndex = xs.indexWhere { inner =>
innerIndex = inner.indexOf(c)
innerIndex > -1
}
(outerIndex, innerIndex)
}
This assumes that you only need the first occurence of that character.
indexWhere
terminates as soon as the innerIndex is larger than -1.
Maybe another possibility, which goes more in your recursive direction (and without a mutable var), but also with minimal iterations:
@tailrec
def search(xs: Vector[Vector[Char]], c: Char, n: Int = 0): (Int, Int) = {
if (n > xs.size - 1) (-1, -1)
else
xs(n).indexOf(c) match {
case -1 => search(xs, c, n + 1)
case i => (n, i)
}
}
Upvotes: 2