Scala: анализ массива String для класса case - PullRequest
0 голосов
/ 28 ноября 2018

Я создал такой класс дел:

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

Как я могу проанализировать данные из массивав этом случае класс?Спасибо за вашу помощь!

Ответы [ 2 ]

0 голосов
/ 28 ноября 2018

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

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 пунктов.Я оставлю тебя, чтобы справиться с этим.Кроме того, имена довольно не описательные, так что вы тоже можете это изменить.Они также упадут, если ваши данные не будут сопоставлены с соответствующими .toDouble или toByte или чем-то еще - вы можете справиться и с этим сами:

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 важно, поскольку оно означает, что последний элемент списка равен 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 (по крайней мере, я так не думаю), но это очень удобно, поскольку я все еще иногда использую этот синтаксис :: на работе.

0 голосов
/ 28 ноября 2018

Вы можете продолжить, как показано ниже (я взял упрощенный пример)

Учитывая ваш класс дела и данные (строки)

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

А затем использовать их для преобразования строк в объекты класса case

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

В качестве более расширенного (и обобщенного) подхода вы можете сгенерироватьфункция отображения (разбора) для преобразования токенов в поля вашего case-класса с использованием чего-то вроде reflection, как сказано здесь

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