【问题标题】:How to return a List of case class from a method in scala如何从scala中的方法返回案例类列表
【发布时间】:2020-08-18 16:26:16
【问题描述】:

我的要求是从 src-resources 文件夹中读取 csv 文件并将它们转换为 DF 。我的代码如下。 流派和月份是案例类,我正在使用它来为我的列表提供结构,以便我可以将它们转换为 DF 。在下面的代码中,我应该使用什么来代替 _ 以便我可以获得 List[Genre] 或 List[Month ] 基于将要传递的文件名值。

trait IndexReader[T] {
  def read(filePath: String, sep: String,fileName:String): List[T] = {
    val stream: InputStream = getClass.getResourceAsStream("/" + filePath)
    val lines: Iterator[String] = scala.io.Source.fromInputStream(stream).getLines
    lines.map(line => {normalize(line, sep,fileName)}).toList
  }
  def normalize(line: String, sep: String,fileName:String): T
}

class BrandReader extends IndexReader[_] {
  override def normalize(line: String, sep: String,fileName:String):_ = {
    val lineSplit: Seq[String] = line.split(sep).toList
    val file = fileName match {
      case "indexGenre" => Genre(lineSplit(0), lineSplit(1))
      case "indexMonth" => Month(lineSplit(0), lineSplit(1))
    }
    file
  }
}

【问题讨论】:

  • Scala 3 将具有联合类型,因此您可以使用 Genre|Month,但在此之前,请使用两者通用的特征或抽象类。

标签: scala class case traits


【解决方案1】:

模式匹配不能从不同的分支返回不同的类型。

如果您可以定义由两个案例类扩展的特征,您可以尝试

sealed trait GenreOrMonth
case class Genre(s: String, s1: String) extends GenreOrMonth
case class Month(s: String, s1: String) extends GenreOrMonth

class BrandReader extends IndexReader[GenreOrMonth] {
  override def normalize(line: String, sep: String, fileName: String): GenreOrMonth = ...
}

否则返回类型只是Product

class BrandReader extends IndexReader[Product] {
  override def normalize(line: String, sep: String, fileName: String): Product = ...
}

Any

class BrandReader extends IndexReader[Any] {
  override def normalize(line: String, sep: String, fileName: String): Any = ...
}

另一种选择是使用Either

case class Genre(s: String, s1: String)
case class Month(s: String, s1: String)

class BrandReader extends IndexReader[Either[Genre, Month]] {
  override def normalize(line: String, sep: String, fileName:String): Either[Genre, Month] = {
    val lineSplit: Seq[String] = line.split(sep).toList
    val file = fileName match {
      case "indexGenre" => Left(Genre(lineSplit(0), lineSplit(1)))
      case "indexMonth" => Right(Month(lineSplit(0), lineSplit(1)))
    }
    file
  }
}

How to define "type disjunction" (union types)?

【讨论】:

  • 嗨 Dmytro,我尝试了第一个解决方案 seal trait。当我调用 read 方法并将 List[IndexFiles] 转换为 DF 时,它什么都没有。 val linesF: Map[String, Nothing] = file_path.map(x=>x._1 -> new BrandReader().read(file_path(x._1), file_sep,x._1).toDF())
  • @minnu 无法复制。您的带有val linesF... 的代码 sn-p 不是独立的。如果您需要进一步的帮助,请提供独立的代码。
  • 当我调用 read 方法如下 val LinesF:List[GenreOrMonth] = new BrandReader().read("generafile.csv", ";","indexGenre") 我怎么能转换为数据框。当我添加 .toDF() 它不起作用
  • @minnu .toDF()List[String] 一起使用,new BrandReader().read(...)List[GenreOrMonth]List[Genre]List[Month] .toDF() 也不起作用。你打算如何让.toDF()List[Genre]List[Month] 一起工作?你的意思是new BrandReader().read("generafile.csv", ";","indexGenre").map(_.toString).toDF()
【解决方案2】:

这是一个使用无形的想法;我已经简化了您正在做的事情,因此我可以提供一个可运行的示例。我在下面显示代码,或单击链接查看它的实际效果。

https://scalafiddle.io/sf/BhyfPyu/1

import shapeless._

case class Genre(category:String, category2:String)
case class Month(month:String, year:String)

abstract class Reader {
  
  def read: (String,String) = ("String1","String2")
  
  val genTuple = Generic[(String,String)]

  def readT[T](implicit genT:Generic.Aux[T,String :: String :: HNil]): T= {
    val hList: (String :: String :: HNil) = genTuple.to(read)
    
    genT.from(hList)
  }
  
  def main(s:String) = {
    s match {
      case "GENRE" => readT[Genre]
      case "MONTH" => readT[Month]
      case _ => throw new IllegalArgumentException(s"$s not supported")
    }
  }
  
}


object ReaderImpl extends Reader

println(ReaderImpl.main("GENRE"))
println(ReaderImpl.main("MONTH"))

【讨论】:

  • 我猜如果你显式调用 genTuple 隐含意义不大。
  • 不,你是对的,那是我没有在我身后整理!好地方。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-10
  • 1970-01-01
  • 2023-03-12
  • 2020-12-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多