Как выйти из (заблокировать) вложенные итераторы после того, как значение найдено? - PullRequest
1 голос
/ 14 июля 2020

У меня есть такой код с вложенными итераторами. Я хочу остановиться, выйти из foreach / map и return (value), если find возвращает и Some (value), и продолжить, если None. Какой здесь правильный подход?

Изменить: Вот полная функция

Line Schema: line_id, name

Stop Schema: stop_id, x, y

Time Schema: line_id, stop_id, time

Учитывая время и x, y, я хочу найти line_id и name. Итак, пока я застрял с получением 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")
  }

Также есть какие-либо улучшения в коде?

еще одна вещь, которую я заметил, это то, что если я провожу проверку длины / размера на итераторе stopIds он исчерпывает итератор, и дальнейшая обработка невозможна. Итак, как я могу узнать, вернул ли первый фильтр 0?

Ответы [ 3 ]

5 голосов
/ 14 июля 2020

Ваш logi c может быть выражен без перерывов примерно так (я не копался в контексте домена, поскольку он явно не имеет значения, и вы можете настроить его самостоятельно):

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

В качестве побочного примечания - я бы порекомендовал здесь подходящую библиотеку CSV, поскольку это решение не является пуленепробиваемым и сломается, если, например, кто-то создаст файл с экранированной комой.

2 голосов
/ 14 июля 2020

Это к тому, что вам нужно?

import scala.util.{Using,Try}  //Scala 2.13.x

def findVehicle(time: String, x: String, y: String) : Try[String] =
  Using.Manager { use =>
    //open the files to be read
    val stopsFile = use(io.Source.fromFile("stops.csv"))
    val timesFile = use(io.Source.fromFile("times.csv"))
    val linesFile = use(io.Source.fromFile("lines.csv"))

    //read stops file, collect all IDs for given x & y
    val stopIds : Set[String] = 
      stopsFile.getLines().drop(1).map(_.split(",").map(_.trim))
               .filter(arr => arr(1) == x && arr(2) == y)
               .map(_.head).toSet

    if (stopIds.isEmpty)
      throw new Exception(s"No stop ID for $x and $y")

    //read times file only until 1st matching time
    val lineID : String =
      timesFile.getLines().drop(1).map(_.split(",").map(_.trim))
               .find(col => stopIds(col(1)) && col(2) == time)
               .fold(
      throw new Exception(s"No Vehicle at time $time")
                    )(_.head)

    //read lines file only until 1st matching line ID
    linesFile.getLines().drop(1).map(_.split(",").map(_.trim))
             .find(_(0) == lineID)
             .fold(
      throw new Exception(s"No vechile found with line ID $lineID")
                  )(_(1))  //name
  }
0 голосов
/ 14 июля 2020

Поскольку ваш вопрос немного абстрактен, вот мои предложения

  • используйте перерыв, чтобы выйти из l oop

  • не обязательно прочтите файл для каждой итерации, которую вы можете прочитать один раз, и использовать его, поэтому поместите его за пределы l oop

    val iter = Source.fromFile("abc.csv").getLines().drop(1)
    val cols = iter.map(_.split(",").map(_.trim))
    import util.control.Breaks._
    val stopIds = cols.filter(arr=> arr(1) == x && arr(2) == y)
    if(stopIds.length>0) {
      stopIds.map{ arr=>
        cols.find(col=> col(1)==arr(0) && col(2)==time) match {
          case Some(value) => // your code
            break
          case None => NotFound(s"No Vehicle available at the given time ${time}")
        }
      }
    } else {
      NotFound(s"No stop found at given x=${x} and y=${y}")
    }
    

Мы можем продолжить оптимизацию, если вы дадите образец ввода и вывода

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...