I have some code like this with nested Iterators. I want to stop, get out of foreach/map and return(value) if the find returns and Some(value), and continue to incase of None. What's the right approach here?
Edit: Here's the full function
Line Schema: line_id, name
Stop Schema: stop_id, x, y
Time Schema: line_id, stop_id, time
Given time and x,y I want to find line_id and name. So for the time being Im stuck with getting valueWhichImTryingToGet.
def findVehicle(time: String, x: String, y: String) = {
val stopIter = Source.fromFile("stops.csv").getLines().drop(1)
val stopCols = stopIter.map(_.split(",").map(_.trim))
val stopIds = stopCols.filter(arr => arr(1) == x && arr(2) == y)
val valueWhichImTryingToGet = stopIds.map { arr =>
val iter = Source.fromFile("times.csv").getLines().drop(1)
val cols = iter.map(_.split(",").map(_.trim))
cols.find(col => col(1) == arr(0) && col(2) == time) match {
case Some(value) => value(0) //String type
case None => NotFound(s"No Vehicle available at the given time ${time}")
}
}
val lineIter = Source.fromFile("lines.csv").getLines().drop(1)
val lineCols = lineIter.map(_.split(",").map(_.trim))
lineCols.find(_(0) == valueWhichImTryingToGet ).getOrElse("No vechile can be found with the given information")
}
Also any improvements in the code?
one more thing I noticed is that If I do the length/size check on iterator stopIds it exhausts the iterator and no further processing can happen. So how can I find if the first filter returned 0?
Your logic can be expressed without breaks more or less like this (I didn't dig into domain context, as it is apparently irrelevant and you can adjust it yourself):
// instead of running whole code for all entries we can just check
// if value exist within some set of values
val stopIds = cols.filter(arr=> arr(1) == x && arr(2) == y).map(_(0)).toSet
Source
.fromFile("abc.csv")
.getLines()
.drop(1) // drop headers
.map { line =>
line.split(",").map(_.trim).toList
}
.collectFirst {
// find first entry which has second value from stopIds set
// and third value equal to expected time
case _ :: id :: atTime :: _ if stopIds.contains(id) && atTime == time =>
Vehicle(value(0), value(1))
}
.getOrElse {
// if no matching value is found fall back on "not found" case
NotFound(s"No Vehicle available at the given time ${time}")
}
As a side note - I would recommend some proper CSV library here as this solution is not bulletproof, and would break if e.g. someone created a file with an escaped coma.