重构Scala的建议-我能消除foreach循环中使用的变量吗
我想了解一些关于如何重构一些Scala代码以使其更优雅、更地道的Scala的建议 我有一个函数重构Scala的建议-我能消除foreach循环中使用的变量吗,scala,apache-spark,Scala,Apache Spark,我想了解一些关于如何重构一些Scala代码以使其更优雅、更地道的Scala的建议 我有一个函数 def joinDataFramesOnColumns(joinColumns: Seq[String]) : org.apache.spark.sql.DataFrame 它通过将Seq[org.apache.spark.sql.DataFrame]连接到joinColumns上来对其进行操作。以下是函数定义: 隐式类SequenceOfDataFramesdataFrames:Seq[DataF
def joinDataFramesOnColumns(joinColumns: Seq[String]) : org.apache.spark.sql.DataFrame
它通过将Seq[org.apache.spark.sql.DataFrame]连接到joinColumns上来对其进行操作。以下是函数定义:
隐式类SequenceOfDataFramesdataFrames:Seq[DataFrame]{
def joinDataFramesOnColumnsjoinColumns:Seq[String]:数据帧={
val emptyDataFrame=SparkSession.builder.getOrCreate.emptyDataFrame
val nonEmptyDataFrames=dataFrames.filter!=emptyDataFrame
如果非EmptyDataFrames.isEmpty{
空数据框
}
否则{
如果joinColumns.isEmpty{
返回非空数据帧。减少交叉连接_
}
NoneEmptyDataFrames.reduce\连接\连接列
}
}
}
我有一些单元测试都成功了:
类功能生成器DataFrameExtensionTest扩展了WordSpec{
val值=序号
Row0,BasketA,香蕉,Jack,
第二排,篮筐,橘子,杰克,
第二排,巴斯克,橘子,吉尔,
第三排,篮子,橘子,杰克,
第四排,篮筐,橘子,杰克,
第四排,篮筐,苹果,杰克,
第四排,篮子,香蕉,吉尔
val schema=List
StructFieldweeksPrior,IntegerType,true,
StructFieldbasket,StringType,true,
StructFieldProduct,StringType,true,
StructFieldCustomer,StringType,true
val fruitDf=spark.createDataFrame
spark.sparkContext.parallelizefruitValues,
结构类型模式
.带有ColumnDate、UDFDATE、SubweekSlitDayPrior或Oasat、ColweekPrior
FeatureGenerator.SequenceOfDataFrames应{
在中的一组指定列上联接多个数据帧{
val sequenceOfDataFrames=Seq[DataFrame]
exportedf.withcolumn重命名为Weeksprior、Weeksprior 1、,
exportedf.withcolumn重命名为Weeksprior、Weeksprior 2、,
exportedf.withcolumn重命名为Weeksprior、Weeksprior或3,
exportedf.withcolumn重命名为weeksprior、weeksprior或4,
exportedf.withcolumn重命名为Weeksprior、Weeksprior或5
val joinedDataFrames=数据框的序列。joinedDataFrames列组合、产品、客户、日期
assertjoinedDataFrames.columns.length==9
assertjoinedDataFrames.columns.containsbasket
assertjoinedDataFrames.columns.containsProduct
assertjoinedDataFrames.columns.ContainesStomer
assertjoinedDataFrames.columns.containsDate
assertjoinedDataFrames.columns.ContainesWebSprior1
assertjoinedDataFrames.columns.ContainesWebSprior2
assertjoinedDataFrames.columns.ContainesWebSprior3
assertjoinedDataFrames.columns.ContainesWebSprior4
assertjoinedDataFrames.columns.ContainesWebSprior5
}
当传递一个包含一个数据帧的列表时,返回中相同的数据帧{
val sequenceOfDataFrames=Seq[DataFrame]fruitDf
val joinedDataFrame=数据框的序列。JoinedDataFramesOnColumnsSeqbasket,产品
assertjoinedDataFrame.columns.sorted==fruitDf.columns.sorted
assertjoinedDataFrame.count==FrootDf.count
}
当传递一个空的数据帧列表时,返回一个空的数据帧{
val joinedDataFrame=Seq[DataFrame]。JoinedDataFramesOnColumnsSeqbasket
assertjoinedDataFrame==spark.emptyDataFrame
}
当传递一个空的joinColumns列表时,返回交叉连接的数据帧{
val sequenceOfDataFrames=Seq[DataFrame]FrootDf,FrootDf,FrootDf
val joinedDataFrame=sequenceOfDataFrames.JoineDataFramesonColumnsSeq[String]
assertjoinedDataFrame.count==scala.math.powfruitDf.count,sequenceOfDataFrames.size
assertjoinedDataFrame.columns.size==FrootDF.columns.size*sequenceOfDataFrames.size
}
}
}
这一切都很好,直到它因为这个Spark错误而开始出错:当连接列具有相同名称时,在某些情况下可能会导致错误
解决方法是将列别名为其他内容,因此我重新编写了这样的函数,该函数为联接列别名,进行联接,然后重新命名它们:
隐式类SequenceOfDataFramesdataFrames:Seq[DataFrame]{
def joinDataFramesOnColumnsjoinColumns:Seq[String]:数据帧={
val emptyDataFrame=SparkSession.builder.getOrCreate.emptyDataFrame
val nonEmptyDataFrames=dataFrames.filter!=emptyDataFrame
如果非EmptyDataFrames.isEmpty{
空数据框
}
否则{
如果joinColumns.isEmpty{
返回非空数据帧。减少交叉连接_
}
/*
下面可怕的、粗糙的、不合法的代码在理想情况下可以简单地存在:
NoneEmptyDataFrames.reduce\连接\连接列
但是,在某些特定情况下,由于spark中的错误,该功能将失败,
看见https://issues.apache.org/jira/browse/SPARK-25150 详情
*/
val aliasSuffix=_别名
val aliasedJoinColumns=joinColumns.mapjoinColumn=>joinColumn+aliasSuffix
var aliasedNonEmptyD
ataFrames:Seq[DataFrame]=Seq
nonEmptyDataFrames.foreach
非空数据帧=>{
var tempNonEmptyDataFrame=nonEmptyDataFrame
每小时
joinColumn=>{
tempNonEmptyDataFrame=tempNonEmptyDataFrame.withColumnRenamedjoinColumn,joinColumn+别名后缀
}
aliasedNonEmptyDataFrames=aliasedNonEmptyDataFrames:+TempNonemptyDataFrames
}
var joinedAliasedNonEmptyDataFrames=aliasedNonEmptyDataFrames.reduce\u0.join\u0,aliasedJoinColumns
每小时
joinColumn=>joinedAliasedNonEmptyDataFrames=joinedAliasedNonEmptyDataFrames.WithColumnRename
joinColumn+别名后缀,joinColumn
连接的非空数据帧
}
}
}
测试仍然通过,所以我对它相当满意,但我正在查看那些变量以及在每次迭代中将结果分配回该变量的循环。。。并发现它们相当不雅,相当丑陋,尤其是与函数的原始版本相比。我觉得必须有一种方法来写这篇文章,这样我就不必使用vars,但是经过一些尝试和错误之后,这是我能做的最好的了
有人能提出一个更优雅的解决方案吗?作为一名Scala开发新手,这将真正帮助我更加熟悉解决此类问题的惯用方法
欢迎对代码的其余部分(如测试)提出任何建设性意见感谢@Duelist,他建议使用foldLeft,这反过来又促使我调整代码,以消除VAR: 隐式类SequenceOfDataFramesdataFrames:Seq[DataFrame]{ def joinDataFramesOnColumnsjoinColumns:Seq[String]:数据帧={ val emptyDataFrame=SparkSession.builder.getOrCreate.emptyDataFrame val nonEmptyDataFrames=dataFrames.filter!=emptyDataFrame 如果非EmptyDataFrames.isEmpty{ 空数据框 } 否则{ 如果joinColumns.isEmpty{ 返回非空数据帧。减少交叉连接_ } /* 理想情况下,以下代码的存在形式如下: NoneEmptyDataFrames.reduce\连接\连接列 但是,在某些特定情况下,由于spark中的错误,该功能将失败, 看见https://issues.apache.org/jira/browse/SPARK-25150 详情 因此,此代码为joinColumns添加别名,执行连接,然后重命名 将别名列恢复为其原始名称 */ val aliasSuffix=_别名 val aliasedJoinColumns=joinColumns.mapjoinColumn=>joinColumn+aliasSuffix val joinedAliasedNonEmptyDataFrames=nonEmptyDataFrames.foldLeftSeq[DataFrame]{ tempDf,nonEmptyDataFrame=>tempDf:+joinColumns.foldLeftnonEmptyDataFrame{ tempDf2,joinColumn=>tempDf2.withColumnRenamedjoinColumn,joinColumn+aliasSuffix } }.reduce\u.join\u,别名JoinColumns joinColumns.FoldleftJoinedAliasedNoneEmptyDataFrames{ tempDf,joinColumn=>tempDf.withColumnRenamedjoinColumn+别名后缀,joinColumn } } } }
通过将两个语句合并为一个语句,从而消除val JoinedaliasedNoneEmptyDataFrames,我本可以更进一步,但我更喜欢使用临时val带来的清晰性。谢谢@Duelist,他建议我使用foldLeft,这反过来又使我调整了代码,以消除VAR: 隐式类SequenceOfDataFramesdataFrames:Seq[DataFrame]{ def joinDataFramesOnColumnsjoinColumns:Seq[String]:数据帧={ val emptyDataFrame=SparkSession.builder.getOrCreate.emptyDataFrame val nonEmptyDataFrames=dataFrames.filter!=emptyDataFrame 如果非EmptyDataFrames.isEmpty{ 空数据框 } 否则{ 如果joinColumns.isEmpty{ 返回非空数据帧。减少交叉连接_ } /* 理想情况下,以下代码的存在形式如下: NoneEmptyDataFrames.reduce\连接\连接列 但是,在某些特定情况下,由于spark中的错误,该功能将失败, 看见https://issues.apache.org/jira/browse/SPARK-25150 详情 因此,此代码为joinColumns添加别名,执行连接,然后重命名 将别名列恢复为其原始名称 */ val aliasSuffix=_别名 val aliasedJoinColumns=joinColumns.mapjoinColumn=>joinColumn+aliasSuffix val joinedAliasedNonEmptyDataFrames=nonEmptyDataFrames.foldLeftSeq[DataFrame]{ tempDf,nonEmptyDataFrame=>tempDf:+joinColumns.foldLeftnonEmptyDataFrame{ tempDf2,joinColumn=>tempDf2.withColumnRenamedjoinColumn,joinColumn+aliasSuffix } }.reduce\u.join\u,别名JoinColumns joinColumns.FoldleftJoinedAliasedNoneEmptyDataFrames{ tempDf,joinColumn=>tempDf.withColumnRenamedjoinColumn+别名后缀,joinColumn } } } } 我
通过将两个语句合并为一个语句,从而消除val JoinedaliasedNoneEmptyDataFrames,本可以更进一步,但我更喜欢使用临时val带来的清晰度。我认为您可以使用方法将var替换为foreach。我已经发布了基于foldLeftI的答案,我认为您可以使用方法将var替换为foreach。我已经发布了基于foldLeft的答案