繁体   English   中英

Scala:将字符串数组解析为案例类

[英]Scala: Parsing Array of String to a case class

我已经创建了一个案例类:

def case_class(): Unit = {
   case class StockPrice(quarter : Byte,
                      stock : String,
                      date : String,
                      open : Double,
                      high : Double,
                      low : Double,
                      close : Double,
                      volume : Double,
                      percent_change_price : Double,
                      percent_change_volume_over_last_wk : Double,
                      previous_weeks_volume : Double,
                      next_weeks_open : Double,
                      next_weeks_close : Double,
                      percent_change_next_weeks_price : Double,
                      days_to_next_dividend : Double,
                      percent_return_next_dividend : Double
                     )

而且我有成千上万的线像这样的字符串数组:

1,AA,1/7/2011,$15.82,$16.72,$15.78,$16.42,239655616,3.79267,,,$16.71,$15.97,-4.42849,26,0.182704

1,AA,1/14/2011,$16.71,$16.71,$15.64,$15.97,242963398,-4.42849,1.380223028,239655616,$16.19,$15.79,-2.47066,19,0.187852

1,AA,1/21/2011,$16.19,$16.38,$15.60,$15.79,138428495,-2.47066,-43.02495926,242963398,$15.87,$16.13,1.63831,12,0.189994

1,AA,1/28/2011,$15.87,$16.63,$15.82,$16.13,151379173,1.63831,9.355500109,138428495,$16.18,$17.14,5.93325,5,0.185989

如何将Array中的数据解析到该case类中? 谢谢您的帮助!

您可以按照以下步骤进行操作(我以简化示例为例)

给定您的案例类别和数据(行)

// Your case-class
case class MyCaseClass(
  fieldByte: Byte,
  fieldString: String,
  fieldDouble: Double
)

// input data
val lines: List[String] = List(
  "1,AA,$1.1",
  "2,BB,$2.2",
  "3,CC,$3.3"
)

注意 :您可以将文本文件中的行读取

val lines = Source.fromFile("my_file.txt").getLines.toList

您可以使用一些实用程序方法进行映射(清理和解析)

// remove '$' symbols from string
def removeDollars(line: String): String = line.replaceAll("\\$", "")

// split string into tokens and
// convert into MyCaseClass object
def parseLine(line: String): MyCaseClass = {
  val tokens: Seq[String] = line.split(",")
  MyCaseClass(
    fieldByte = tokens(0).toByte,
    fieldString = tokens(1),
    fieldDouble = tokens(2).toDouble
  )
}

然后使用它们将字符串转换为案例类对象

// conversion
val myCaseClassObjects: Seq[MyCaseClass] = lines.map(removeDollars).map(parseLine)

作为一种更高级(通用)的方法 ,您可以生成映射(解析)函数,以使用诸如reflection类的东西将令牌转换为案例类的字段,如此处所述

这是一种方法。 我建议将您所做的所有事情都拆分为许多小的,易于管理的函数,否则,如果所有东西都开始抛出异常,您就会迷失方向去弄清楚哪里出了问题。 数据设置:

val array = Array("1,AA,1/7/2011,$15.82,$16.72,$15.78,$16.42,239655616,3.79267,,,$16.71,$15.97,-4.42849,26,0.182704",
  "1,AA,1/14/2011,$16.71,$16.71,$15.64,$15.97,242963398,-4.42849,1.380223028,239655616,$16.19,$15.79,-2.47066,19,0.187852",
  "1,AA,1/21/2011,$16.19,$16.38,$15.60,$15.79,138428495,-2.47066,-43.02495926,242963398,$15.87,$16.13,1.63831,12,0.189994",
  "1,AA,1/28/2011,$15.87,$16.63,$15.82,$16.13,151379173,1.63831,9.355500109,138428495,$16.18,$17.14,5.93325,5,0.185989")

case class StockPrice(quarter: Byte, stock: String, date: String, open: Double,
  high: Double, low: Double, close: Double, volume: Double, percent_change_price: Double,
  percent_change_volume_over_last_wk: Double, previous_weeks_volume: Double,
  next_weeks_open: Double, next_weeks_close: Double, percent_change_next_weeks_price: Double,
  days_to_next_dividend: Double, percent_return_next_dividend: Double
)

Array[String]转换为Array[List[String]]并处理任何空字段的函数(我在这里假设您希望空字段为0根据需要更改此值):

def splitArray(arr: Array[String]): Array[List[String]] = {
  arr.map(
    _.replaceAll("\\$", "")         // Remove $
      .split(",")                   // Split by ,
      .map {
        case x if x.isEmpty => "0"  // If empty
        case y => y                 // If not empty
      }
      .toList
  )
}

List[String]转换为StockPrice 请注意,如果“列表”长度不完全是16个项目,则此方法会失效。 我让你处理任何一个。 另外,名称是非描述性的,因此您也可以更改它。 如果您的数据未映射到相关的.toDoubletoByte或其他任何内容,它也将崩溃-您也可以自己处理:

def toStockPrice: List[String] => StockPrice = {
  case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: n :: o :: p :: Nil =>
    StockPrice(a.toByte, b, c, d.toDouble, e.toDouble, f.toDouble, g.toDouble, h.toDouble, i.toDouble, j.toDouble,
      k.toDouble, l.toDouble, m.toDouble, n.toDouble, o.toDouble, p.toDouble)
}

一个很好的功能,可以将所有这些组合在一起:

def makeCaseClass(arr: Array[String]): Seq[StockPrice] = {
  val splitArr: Array[List[String]] = splitArray(arr)
  splitArr.map(toStockPrice)
}

输出:

println(makeCaseClass(array))

//ArraySeq(
// StockPrice(1,AA,1/7/2011,15.82,16.72,15.78,16.42,2.39655616E8,3.79267,0.0,0.0,16.71,15.97,-4.42849,26.0,0.182704), 
// StockPrice(1,AA,1/14/2011,16.71,16.71,15.64,15.97,2.42963398E8,-4.42849,1.380223028,2.39655616E8,16.19,15.79,-2.47066,19.0,0.187852), 
// StockPrice(1,AA,1/21/2011,16.19,16.38,15.6,15.79,1.38428495E8,-2.47066,-43.02495926,2.42963398E8,15.87,16.13,1.63831,12.0,0.189994), 
// StockPrice(1,AA,1/28/2011,15.87,16.63,15.82,16.13,1.51379173E8,1.63831,9.355500109,1.38428495E8,16.18,17.14,5.93325,5.0,0.185989)
//)

编辑:

解释a :: b :: c .....位-如果您知道列表的大小,这是一种为列表或序列中的项目分配名称的方法。

val ls = List(1, 2, 3)
val a :: b :: c :: Nil = List(1, 2, 3)
println(a == ls.head) // true
println(b == ls(1)) // true
println(c == ls(2)) // true

请注意, Nil很重要,因为它表示List的最后一个元素为Nil。 没有它, c将等于List(3)因为任何List的其余部分都被分配给定义中的最后一个值。

您可以像我一样在模式匹配中使用它,以便对结果进行处理:

val ls = List(1, "b", true)
ls match {
  case a :: b :: c if c == true => println("this will not be printed")
  case a :: b :: c :: Nil if c == true => println(s"this will get printed because c == $c")
} // not exhaustive but you get the point

如果知道列表中每个元素的内容,也可以使用它,例如:

val personCharacteristics = List("James", 26, "blue", 6, 85.4, "brown")
val name :: age :: eyeColour :: otherCharacteristics = personCharacteristics
println(s"Name: $name; Age: $age; Eye colour: $eyeColour")
// Name: James; Age: 26; Eye colour: blue

显然,这些示例非常琐碎,与您作为专业Scala开发人员所看到的不完全相同(至少我没有),但是要意识到这一点很方便,因为我有时仍然在工作中使用::语法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM