CSV 处理的模式匹配



我有一个包含三列的csv文件,我想将第三列放入迭代器中。我想通过使用 trytoDouble 方法结合模式匹配来过滤掉标头。

def trytoDouble(s: String): Option[Double] = {
  try {
    Some(s.toDouble)
  } catch {
    case e: Exception => None
  }
}
val asdf = Source.fromFile("my.csv").getLines().map(_.split(",").map(_.trim).map(utils.trytoDouble(_))).map{
  _ match {
    case Array(a, b, Some(c: Double)) => c
  }
}

结果在

异常或错误导致运行中止:[Lscala.Option;@2b4a2ec7(类 [Lscala.Option;( 斯卡拉。MatchError: [Lscala.Option;@2b4a2ec7 (of class [Lscala.Option;(

我做错了什么?

尝试使用如下所示StringDouble提取。如果unapply返回Some则匹配,如果返回None则不匹配。

object StringDouble {
  def unapply(str: String): Option[Double] = Try(str.toDouble).toOption
}
val doubles =
  Source.fromFile("my.csv").getLines().map { line =>
    line.split(",").map(_.trim)
  }.map {
    case Array(_, _, StringDouble(d)) => d
  }

它总是会给出scala.MatchError

scala> val url = "/home/knoldus/data/moviedataset.csv"
url: String = /home/knoldus/data/moviedataset.csv
scala> val asdf1 = Source.fromFile(url).getLines().map(_.split(",").map(_.trim).map(trytoDouble(_))).toList
asdf1: List[Array[Option[Double]]] = List(Array(Some(1.0), None, Some(1993.0), Some(3.9), Some(4568.0)), Array(Some(2.0), None, Some(1932.0), Some(3.5), Some(4388.0)), Array(Some(3.0), None, Some(1921.0), Some(3.2), Some(9062.0)), Array(Some(4.0), None, Some(1991.0), Some(2.8), Some(6150.0)), Array(Some(5.0), None, Some(1963.0), Some(2.8), Some(5126.0)), ....

由于它将返回非空迭代器,因此要查看结果,我已将其转换为List。如果您注意到返回类型为 List[Array[Option[Double]]] ,并且您正在尝试与元组 3 数组匹配,但它总是返回 Array[Option[Double]] 。因此,它总是会抛出错误。

试试这个: val asdf = Array("1,2,3","1,b,c").map(_.split(",").map(_.trim).map(trytoDouble(_))).map{ _(2) match { case x:Option[Double] => x } }

只有一个

匹配案例总是会破坏代码。

参见 REPL 示例,

scala> def trytoDouble(s: String): Option[Double] = {
     |   try {
     |     Some(s.toDouble)
     |   } catch {
     |     case e: Exception => None
     |   }
     | }

当您的 CSV 中没有双精度时,

scala> List("a,b,c").map(_.split(",").map(_.trim).map(trytoDouble(_)))
res1:  List[Array[Option[Double]]] = List(Array(None, None, None))

当您将上述结果(Array(None, None, None)(与Array(a, b, Some(c: Double))总是失败时,

scala> List("a,b,c").map(_.split(",").map(_.trim).map(trytoDouble(_))).map { data => 
     | data match {
     |     case Array(a, b, Some(c: Double)) => c
     | }
     | }
scala.MatchError: [Lscala.Option;@22604c7e (of class [Lscala.Option;)
  at .$anonfun$res4$4(<console>:15)
  at .$anonfun$res4$4$adapted(<console>:13)
  at scala.collection.immutable.List.map(List.scala:283)
  ... 33 elided

当有双倍时,

scala> List("a,b,100").map(_.split(",").map(_.trim).map(trytoDouble(_))).map { data => data match { case Array(a, b, Some(c: Double)) => c }}
res5: List[Double] = List(100.0)

您基本上需要检查某些(c(或无。

但是,如果我理解你,你要提取第三个字段作为双精度,这可以这样完成,

scala> List("a,b,100", "100, 200, p").map(_.split(",")).map { case Array(a, b, c) => trytoDouble(c)}.filter(_.nonEmpty)
res14: List[Option[Double]] = List(Some(100.0))

其他答案都很棒,但我刚刚注意到,最小代码更改的替代方法是简单地编写

val asdf = Source.fromFile(fileName).getLines().map(_.split(",").map(_.trim).map( utils.trytoDouble(_))).flatMap{
  _ match {
    case Array(a, b, c) => c
  }
}

或者效率稍微高一点(因为我们只对最后一列感兴趣(:

val asdf = Source.fromFile(fileName).getLines().map(_.split(",")).flatMap{
  _ match {
    case Array(a, b, c) => trytoDouble(c.trim)
  }
}

需要注意的是,此处flatMap将删除None对象。

最新更新