在 Scala 中重新推断序列类型

san*_*4ka 0 types scala

假设我们有一个序列 Any

val seq = Seq(1,2,null)
seq: Seq[Any] = List(1, 2, null)
Run Code Online (Sandbox Code Playgroud)

现在,如果一个过滤器非空元素获得一个新的序列

val cleanSeq = seq.filterNot(_ == null)
cleanSeq: Seq[Any] = List(1, 2)
Run Code Online (Sandbox Code Playgroud)

现在,我想获得与创建新序列时获得的类型相同的类型,例如 cleanSeq

val seq2 = Seq(1,2)
seq2: Seq[Int] = List(1, 2)
Run Code Online (Sandbox Code Playgroud)

我可以Seq[Int]从 开始以某种方式获得cleanSeq吗?

更新

前一个只是一个虚拟示例,我可以有其他类型而不是Int复杂类型示例:Array[Map[String, Float]].

我能做的唯一假设是我有一个可能包含空值的序列。但是序列中的其他元素除了Any. 删除空值后,我想找到常见的超类型。

更新

用例

我想从具有名称和值的列创建一个火花数据框。这些值存储在一个Seq. 从Seq我想派生数据框的模式的类型。

列的定义

  import reflect.runtime.universe._
  import org.apache.spark.sql.catalyst.ScalaReflection
  import org.apache.spark.sql.types._

  case class InternalColumn[A: TypeTag](colName: String, col: Seq[A]) {

    private def getType: DataType = ScalaReflection.schemaFor[A].dataType

    def genStructField: StructField = StructField(colName, getType)
  }
Run Code Online (Sandbox Code Playgroud)

数据框的创建:

 def createDF[T](data: Seq[T], sch: StructType): DataFrame = {
    val dataRow: Seq[Row] = data.map {
      case row: Row => row
      case prod: Product => Row(prod.productIterator.toSeq: _*)
      case d => Row(d)
    }
    spark.createDataFrame(sc.makeRDD(dataRow), sch)
  }
Run Code Online (Sandbox Code Playgroud)

用法

  def createFromColumns(data: Seq[InternalColumn[_]]): DataFrame = {
    def loop(schema: StructType, cols: Seq[InternalColumn[_]]): StructType = cols.toList match {
      case Nil => schema
      case h :: t => loop(schema.add(h.genStructField), t)
    }

    val sch: StructType = loop(new StructType(), data)
    createDF(data.map(_.col).transpose.map(Row.fromSeq), sch)
  }

val df = createFromColumns(List(InternalColumn("c1", List(1,2,3)), InternalColumn("c2", List("a", "b", "c"))))

scala> df.show()
+---+---+
| c1| c2|
+---+---+
|  1|  a|
|  2|  b|
|  3|  c|
+---+---+


scala> df.printSchema
root
 |-- c1: integer (nullable = true)
 |-- c2: string (nullable = true)


Run Code Online (Sandbox Code Playgroud)

到目前为止,这很好用。但是人们可能想要创建一个包含空值的列的数据框。如果具有空值的列可以为空,例如它具有StringType那么它仍然有效。

当您在不可为空的列中有空值时会出现问题,例如:

scala> val df = createFromColumns(List(InternalColumn("c1", List(1,2,null)), InternalColumn("c2", List("a", "b", "c"))))
java.lang.UnsupportedOperationException: Schema for type Any is not supported
Run Code Online (Sandbox Code Playgroud)

这就是我想在删除所有空值时推断序列类型的原因。

Thi*_*ilo 5

您应该首先尝试不要丢失正确的类型(这Seq[Any]是一种糟糕的代码味道,就像使用一样null),但是要找回真正的类型,您可以进行运行时类型检查:

 seq.collect{ case x: Int => x } 
Run Code Online (Sandbox Code Playgroud)

这将Seq[Int]再次抛出所有不是Int.

如果您实际上没有其他东西,而只有Intnull,请考虑Option[Int]改用:

val seq: Seq[Option[Int]] = Seq(Some(1), Some(2), None)
// then you can do
seq.flatten // gives you Seq[Int] 
Run Code Online (Sandbox Code Playgroud)

删除空值后,我想找到常见的超类型。

您确实必须知道(在编写程序时)该通用超类型应该是什么。然后,您可以编写针对该类型进行检查的代码。

如果你不知道你想要一个Seq[Int]结果,你会声明结果的类型是什么?请记住,这些泛型类型(Intin Seq[Int])在运行时被删除,并且仅存在于编译时进行静态类型检查的好处。

  • 是的,这种动态类型编码在 Scala 中非常罕见。我们通常首先会尽力避免这种情况发生。也许您可以扩展从哪里获取这些数据。 (4认同)