1

不変のデータ構造に保存されるcsvファイルを読み込んでいます。各列は入り口です。各入口には1つの駅があります。各ステーションには複数の入り口を設けることができます。以下に示すダブルパスの代わりにシングルパスでこれを行う方法はありますか?

object NYCSubwayEntrances {
  def main(args: Array[String]) = {
    import com.github.tototoshi.csv.CSVReader
    //http://www.mta.info/developers/data/nyct/subway/StationEntrances.csv
    val file = new java.io.File("StationEntrances.csv")
    val reader = CSVReader.open(file)
    reader.readNext //consume headers
    val entranceMap = list2multimap(
      reader.all map {
        case fields: List[String] => 
          // println(fields)
          (
            fields(2), 
            Entrance(
              fields(14).toBoolean,
              Option(fields(15)),
              fields(16).toBoolean,
              fields(17),
              fields(18) match {case "YES" => true case _ => false},
              fields(19) match {case "YES" => true case _ => false},
              fields(20),
              fields(21),
              fields(22),
              fields(23),
              fields(24).toInt,
              fields(25).toInt
            )
          )
        }
      )
    reader.close
    val reader2 = CSVReader.open(file)
    reader2.readNext //consume headers
    val stations = reader2.all map { case fields: List[String] =>
      Station(
        fields(2),
        fields(0),
        fields(1),
        colate(scala.collection.immutable.ListSet[String](
          fields(3),
          fields(4),
          fields(5),
          fields(6),
          fields(7),
          fields(8),
          fields(9),
          fields(10),
          fields(11),
          fields(12),
          fields(13)
        )),
        entranceMap(fields(2)).toList
      )
    }
    reader2.close

    import net.liftweb.json._
    import net.liftweb.json.Serialization.write
    implicit val formats = Serialization.formats(NoTypeHints)
    println(pretty(render(parse(write(stations.toSet)))))
  }

  import scala.collection.mutable.{HashMap, Set, MultiMap}

  def list2multimap[A, B](list: List[(A, B)]) = 
    list.foldLeft(new HashMap[A, Set[B]] with MultiMap[A, B]){(acc, pair) => acc.addBinding(pair._1, pair._2)}

  def colate(set: scala.collection.immutable.ListSet[String]): List[String] =
    ((List[String]() ++ set) diff List("")).reverse
}

case class Station(name: String, division: String, line: String, routes: List[String], entrances: List[Entrance]) {}
case class Entrance(ada: Boolean, adaNotes: Option[String], freeCrossover: Boolean, entranceType: String, entry: Boolean, exitOnly: Boolean, entranceStaffing: String, northSouthStreet: String, eastWestStreet: String, corner: String, latitude: Integer, longitude: Integer) {}

すべての正しい依存関係を持つsbtプロジェクトは、 https://github.com/AEtherSurfer/NYCSubwayEntrancesにあります。

StationEntrances.csvは、http://www.mta.info/developers/sbwy_entrance.htmlから取得されました

4

1 に答える 1

1

次のスニペットがあります。この最初のソリューションはgroupBy、同じ駅に関連する入口をグループ化するために使用します。行がソートされているとは想定していません。ファイルの読み取りは1回だけですが、実際には3回のパスを実行します(1回はメモリ内のすべてを読み取り、1回groupByはステーションを作成します)。Rowエクストラクタのコードについては、最後を参照してください。

val stations = {
  val file = new java.io.File("StationEntrances.csv")
  val reader = com.github.tototoshi.csv.CSVReader.open(file)
  val byStation = reader
    .all     // read all in memory
    .drop(1) // drop header
    .groupBy {
      case List(division, line, station, _*) => (division, line, station)
    }
  reader.close
  byStation.values.toList map { rows =>
    val entrances = rows map { case Row(_, _, _, _, entrance) => entrance }
    rows.head match {
      case Row(division, line, station, routes, _) =>
        Station(
          division, line, station,
          routes.toList.filter(_ != ""),
          entrances)
    }
  }
}

このソリューションは、行がソートされており、ファイルを読み取るときに1回のパスのみを実行して結果リストを作成するため、より高速である必要があることを前提としています。

val stations2  = {
  import collection.mutable.ListBuffer
  def processByChunk(iter: Iterator[Seq[String]], acc: ListBuffer[Station])
           : List[Station] = {
    if (!iter.hasNext) acc.toList
    else {
      val head = iter.next
      val marker = head.take(3)
      val (rows, rest) = iter.span(_ startsWith marker)
      val entrances = (head :: rows.toList) map {
        case Row(_, _, _, _, entrance) => entrance
      }
      val station = head match {
        case Row(division, line, station, routes, _) =>
          Station(
            division, line, station,
            routes.toList.filter(_ != ""),
            entrances)
      }
      processByChunk(rest, acc += station)
    }
  }
  val file = new java.io.File("StationEntrances.csv")
  val reader = com.github.tototoshi.csv.CSVReader.open(file)
  val stations = processByChunk(reader.iterator.drop(1), ListBuffer())
  reader.close
  stations
}           

特定の路線からルート/入口を取得するための専用の抽出機能を作成しました。コードが読みやすくなると思いますが、リストを扱っている場合は、各呼び出しがリストをトラバースする必要があるため、toの呼び出しは最適ではありませんfields(0)fields(25)エクストラクタはこれを回避します。ほとんどのJavacsvパーサーでは、通常はを取得するArray[String]ため、通常は問題になりません。最後に、csv解析は通常、null文字列を返さないため、if (adaNotes == "") None else Some(adaNotes)の代わりに使用することをお勧めしますOption(adaNotes)

object Row {
  def unapply(s: Seq[String]) = s match {
    case List(division, line, station, rest @ _*) =>
      val (routes,
        List(ada, adaNotes, freeCrossover, entranceType,
          entry, exitOnly, entranceStaffing, northSouthStreet, eastWestStreet,
          corner, latitude, longitude)) = rest splitAt 11 // 11 routes
      Some((
        division, line, station,
        routes,
        Entrance(
          ada.toBoolean, Option(adaNotes),
          freeCrossover.toBoolean, entranceType,
          entry == "YES", exitOnly == "YES",
          entranceStaffing, northSouthStreet, eastWestStreet, corner,
          latitude.toInt, longitude.toInt)))
    case _ => None
  }
}
于 2013-03-20T05:47:36.257 に答える