用于CSV处理的模式匹配

时间:2017-11-17 09:35:27

标签: scala

我有一个包含三列的csv文件,我希望将第三列放入Iterator中。我想通过结合模式匹配使用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;)   scala.MatchError:[Lscala.Option; @ 2b4a2ec7(类[Lscala.Option;]

我做错了什么?

5 个答案:

答案 0 :(得分:0)

尝试使用下面StringDouble之类的extractor。如果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
  }

答案 1 :(得分:0)

只有一个匹配案例总是会破坏代码。

参见REPL示例,

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

当您的CSV中没有Double时,

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

当有Double时,

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)或无。

但是,如果我理解你,你要将第三个字段提取为Double,可以这样做,

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))

答案 2 :(得分:0)

始终会提供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]]],并且您尝试与tuple3的数组匹配,但它始终返回Array[Option[Double]]。 因此,它总是会抛出错误。

答案 3 :(得分:0)

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

答案 4 :(得分: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个对象。