以更好的方式实现 else-if scala 语句
Implement else-if scala statement in a better way
我在 Scala 中开发一个函数,它获取开始日期、截止日期和时区信息作为参数。该函数必须 return 四分之一标签,如下所示:
2020 Q2
可能会发生不同的情况:
- 截止日期为空,从开始日期计算季度标签
- 截止日期从 9999 年开始,从开始日期计算季度标签
- 开始日期为空,从截止日期计算季度标签
- 当开始日期和截止日期都可用时,它们应该相同,return那个季度标签
- 时区信息可以为空,或者两个日期都可以为空,在这些情况下什么也不做
我开始实施 - 暂时忽略了一些要求 -,最终得到了数百万个 if 语句,它们甚至没有按照我想要的方式工作。
当开始日期和截止日期都可用时,它会计算季度和所有内容,以及时区信息。当时区不可用(正确)时它会跳过,但当缺少开始日期或截止日期时它不起作用。
代码:
import java.time.{LocalDateTime,ZoneId}
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit._
import java.time.temporal.IsoFields
object someObject extends Serializable {
def isEmpty(x: String): Boolean = x == null || Option(x.trim).forall(_.isEmpty)
def quarterlyFilter(startDate: String, dueDate: String, timeZone: String): String = {
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
val calculatorTZ = ZoneId.of("GMT+4")
try {
if(isEmpty(dueDate) == true || dueDate.startsWith("9999")) {
val start = LocalDateTime.parse(startDate, formatter)
val incomingTZ = ZoneId.of(timeZone)
val incomingStart = start.atZone(incomingTZ)
val calculatedStartDate = incomingStart.withZoneSameInstant(calculatorTZ).toLocalDate
val quarter = calculatedStartDate.get(IsoFields.QUARTER_OF_YEAR)
val year = calculatedStartDate.getYear
println("only start")
year + " " + quarter
} else if(isEmpty(startDate) == true) {
val due = LocalDateTime.parse(dueDate, formatter)
val incomingTZ = ZoneId.of(timeZone)
val incomingDue = due.atZone(incomingTZ)
val calculatedDueDate = incomingDue.withZoneSameInstant(calculatorTZ).toLocalDate
val quarter = calculatedDueDate.get(IsoFields.QUARTER_OF_YEAR)
val year = calculatedDueDate.getYear
println("only due")
year + " " + quarter
} else {
val start = LocalDateTime.parse(startDate, formatter)
val due = LocalDateTime.parse(dueDate, formatter)
val incomingTZ = ZoneId.of(timeZone)
val incomingStart = start.atZone(incomingTZ)
val incomingDue = due.atZone(incomingTZ)
val calculatedStartDate = incomingStart.withZoneSameInstant(calculatorTZ).toLocalDate
val calculatedDueDate = incomingDue.withZoneSameInstant(calculatorTZ).toLocalDate
val startQuarter = calculatedStartDate.get(IsoFields.QUARTER_OF_YEAR)
val startYear = calculatedStartDate.getYear
val dueQuarter = calculatedDueDate.get(IsoFields.QUARTER_OF_YEAR)
val dueYear = calculatedDueDate.getYear
println("both")
startYear + " " + startQuarter + " vs " + dueYear + " " + dueQuarter
}
} catch {
case e: java.time.zone.ZoneRulesException => {
println("no timeZone info")
null
}
}
}
def checkQuarter: (String,String,String) => String = quarterlyFilter
val getQuarterInfo = udf(checkQuarter)
}
当我运行这个用下面的测试数据:
val startDate = "null"
val currentDate = "2018-09-30 21:59:59"
val timeZone = "Europe/Copenhagen"
someObject.quarterlyFilter(startDate, currentDate, timeZone)
我收到此错误,即使根本不应该进行解析..
java.time.format.DateTimeParseException
你能帮我把这个功能做得更好、更简单吗?提前致谢!
如果我正确理解需求,我会这样做:
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
def quarterForDate(date: LocalDateTime, zoneId: ZoneId) = {
val zoned = date.atZone(zoneId)
s"${zoned.getYear} Q${zoned.get(java.time.temporal.IsoFields.QUARTER_OF_YEAR)}"
}
def calculateQuarter(startDateString: Option[String], dueDateString: Option[String], timeZoneString: Option[String]): Option[String] = {
val startDateOpt = startDateString.flatMap(s => Try(LocalDateTime.parse(s, formatter)).toOption)
val dueDateOpt = dueDateString.flatMap(s => Try(LocalDateTime.parse(s, formatter)).toOption)
val zoneIdOpt = timeZoneString.flatMap(s => Try(ZoneId.of(s)).toOption)
(startDateOpt, dueDateOpt, zoneIdOpt) match {
case (Some(startDate), Some(dueDate), Some(zoneId)) =>
val q1 = quarterForDate(startDate, zoneId)
val q2 = quarterForDate(dueDate, zoneId)
if (q1 == q2) Some(q1) else None
case (Some(startDate), None, Some(zoneId)) =>
Some(quarterForDate(startDate, zoneId))
case (None, Some(dueDate), Some(zoneId)) =>
Some(quarterForDate(dueDate, zoneId))
case _ =>
None
}
}
val startDate = Option("null").map(_.trim).filterNot(_.equalsIgnoreCase("null"))
val currentDate = Option("2018-09-30 21:59:59").map(_.trim).filterNot(_.equalsIgnoreCase("null"))
val timeZone = Option("Europe/Copenhagen").map(_.trim).filterNot(_.equalsIgnoreCase("null"))
calculateQuarter(startDate, currentDate, timeZone)
- 参数是否为空应该在函数之外处理
- 因为 invalid 和 missing 的处理方式相同,所以我会坚持使用 Option
- 可以提取格式逻辑以避免重复
- 不同的情况可以用模式匹配来处理,让逻辑更清晰
即使我的要求有误,这对您来说应该更容易适应这段代码应该做的事情。
好的,让我们看看我们能用它做什么。
首先让我们防止空 timeZone
,因为它很容易短路,我们会很高兴知道我们稍后有一个有效的。这里有两个选择;您可以使用条件 return None
(Java 样式),或者您可以将整个方法的其余部分包装在 flatMap 中(这是我的偏好)。我们还将在此处检查 ZoneRulesException
位并将无效时区视为 "no time zone"(可以说是):
Option(timeZone).flatMap(tz => Try(ZoneId.of(tz)).toOption) flatMap { incomingTZ =>
// rest of method
}
due date is null, calculate quarterly label from start date
due date starts with year 9999, calculate quarterly label from start date
9999?呸。好的,我们可以很好地处理这两种情况:
def parseDate(s: String) = Try(LocalDateTime.parse(startDate, formatter)).toOption
val validDueDate = Option(dueDate).flatMap(parseDate).filter(_.getYear < 9999)
(从技术上讲,您的规范讨论了以 9999 开头的日期字符串,因此我们应该在解析之前 .filterNot(_ startsWith "9999")
。但是这种方式感觉更清晰,并且可以更轻松地适应其他基于日期的过滤。)
开始日期的处理类似,只是没有年份过滤:
val validStartDate = Option(startDate).flatMap(parseDate)
现在我们有两个输入选项。我认为案例的模式匹配在这里可能是最简单的,以计算出标签的日期:
val dateForCalc = (validStartDate, validDueDate) match {
case (Some(sd), Some(dd)) if sd == dd => sd // arbitrary, could pick either
case (Some(sd), Some(dd)) => throw IllegalArgumentException(s"Start Date $sd != Due Date $dd!")
case (Some(sd), None) => sd
case (None, Some(dd)) => dd
case (None, None) => return None // or whatever "do nothing" means
}
太好了,现在我们在 dateForCalc
中有了开始日期或截止日期,只需要从中计算出季度标签。在这一点上,我们可以只使用所有现有的 if-else 块的内容:
val incomingDate = dateForCalc.atZone(incomingTZ)
val calculatedDate = incomingDate .withZoneSameInstant(calculatorTZ).toLocalDate
val quarter = calculatedDate.get(IsoFields.QUARTER_OF_YEAR)
val year = calculatedDate.getYear
Some(year + " " + quarter)
我认为这是一个相当不错的方法。我们预先进行验证、解析和日期比较;这样,当所有这些都完成时,您就可以使用 相关且经过验证的 输入,只需执行计算季度和年度的无聊逻辑即可。
我在 Scala 中开发一个函数,它获取开始日期、截止日期和时区信息作为参数。该函数必须 return 四分之一标签,如下所示:
2020 Q2
可能会发生不同的情况:
- 截止日期为空,从开始日期计算季度标签
- 截止日期从 9999 年开始,从开始日期计算季度标签
- 开始日期为空,从截止日期计算季度标签
- 当开始日期和截止日期都可用时,它们应该相同,return那个季度标签
- 时区信息可以为空,或者两个日期都可以为空,在这些情况下什么也不做
我开始实施 - 暂时忽略了一些要求 -,最终得到了数百万个 if 语句,它们甚至没有按照我想要的方式工作。
当开始日期和截止日期都可用时,它会计算季度和所有内容,以及时区信息。当时区不可用(正确)时它会跳过,但当缺少开始日期或截止日期时它不起作用。
代码:
import java.time.{LocalDateTime,ZoneId}
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit._
import java.time.temporal.IsoFields
object someObject extends Serializable {
def isEmpty(x: String): Boolean = x == null || Option(x.trim).forall(_.isEmpty)
def quarterlyFilter(startDate: String, dueDate: String, timeZone: String): String = {
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
val calculatorTZ = ZoneId.of("GMT+4")
try {
if(isEmpty(dueDate) == true || dueDate.startsWith("9999")) {
val start = LocalDateTime.parse(startDate, formatter)
val incomingTZ = ZoneId.of(timeZone)
val incomingStart = start.atZone(incomingTZ)
val calculatedStartDate = incomingStart.withZoneSameInstant(calculatorTZ).toLocalDate
val quarter = calculatedStartDate.get(IsoFields.QUARTER_OF_YEAR)
val year = calculatedStartDate.getYear
println("only start")
year + " " + quarter
} else if(isEmpty(startDate) == true) {
val due = LocalDateTime.parse(dueDate, formatter)
val incomingTZ = ZoneId.of(timeZone)
val incomingDue = due.atZone(incomingTZ)
val calculatedDueDate = incomingDue.withZoneSameInstant(calculatorTZ).toLocalDate
val quarter = calculatedDueDate.get(IsoFields.QUARTER_OF_YEAR)
val year = calculatedDueDate.getYear
println("only due")
year + " " + quarter
} else {
val start = LocalDateTime.parse(startDate, formatter)
val due = LocalDateTime.parse(dueDate, formatter)
val incomingTZ = ZoneId.of(timeZone)
val incomingStart = start.atZone(incomingTZ)
val incomingDue = due.atZone(incomingTZ)
val calculatedStartDate = incomingStart.withZoneSameInstant(calculatorTZ).toLocalDate
val calculatedDueDate = incomingDue.withZoneSameInstant(calculatorTZ).toLocalDate
val startQuarter = calculatedStartDate.get(IsoFields.QUARTER_OF_YEAR)
val startYear = calculatedStartDate.getYear
val dueQuarter = calculatedDueDate.get(IsoFields.QUARTER_OF_YEAR)
val dueYear = calculatedDueDate.getYear
println("both")
startYear + " " + startQuarter + " vs " + dueYear + " " + dueQuarter
}
} catch {
case e: java.time.zone.ZoneRulesException => {
println("no timeZone info")
null
}
}
}
def checkQuarter: (String,String,String) => String = quarterlyFilter
val getQuarterInfo = udf(checkQuarter)
}
当我运行这个用下面的测试数据:
val startDate = "null"
val currentDate = "2018-09-30 21:59:59"
val timeZone = "Europe/Copenhagen"
someObject.quarterlyFilter(startDate, currentDate, timeZone)
我收到此错误,即使根本不应该进行解析..
java.time.format.DateTimeParseException
你能帮我把这个功能做得更好、更简单吗?提前致谢!
如果我正确理解需求,我会这样做:
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
def quarterForDate(date: LocalDateTime, zoneId: ZoneId) = {
val zoned = date.atZone(zoneId)
s"${zoned.getYear} Q${zoned.get(java.time.temporal.IsoFields.QUARTER_OF_YEAR)}"
}
def calculateQuarter(startDateString: Option[String], dueDateString: Option[String], timeZoneString: Option[String]): Option[String] = {
val startDateOpt = startDateString.flatMap(s => Try(LocalDateTime.parse(s, formatter)).toOption)
val dueDateOpt = dueDateString.flatMap(s => Try(LocalDateTime.parse(s, formatter)).toOption)
val zoneIdOpt = timeZoneString.flatMap(s => Try(ZoneId.of(s)).toOption)
(startDateOpt, dueDateOpt, zoneIdOpt) match {
case (Some(startDate), Some(dueDate), Some(zoneId)) =>
val q1 = quarterForDate(startDate, zoneId)
val q2 = quarterForDate(dueDate, zoneId)
if (q1 == q2) Some(q1) else None
case (Some(startDate), None, Some(zoneId)) =>
Some(quarterForDate(startDate, zoneId))
case (None, Some(dueDate), Some(zoneId)) =>
Some(quarterForDate(dueDate, zoneId))
case _ =>
None
}
}
val startDate = Option("null").map(_.trim).filterNot(_.equalsIgnoreCase("null"))
val currentDate = Option("2018-09-30 21:59:59").map(_.trim).filterNot(_.equalsIgnoreCase("null"))
val timeZone = Option("Europe/Copenhagen").map(_.trim).filterNot(_.equalsIgnoreCase("null"))
calculateQuarter(startDate, currentDate, timeZone)
- 参数是否为空应该在函数之外处理
- 因为 invalid 和 missing 的处理方式相同,所以我会坚持使用 Option
- 可以提取格式逻辑以避免重复
- 不同的情况可以用模式匹配来处理,让逻辑更清晰
即使我的要求有误,这对您来说应该更容易适应这段代码应该做的事情。
好的,让我们看看我们能用它做什么。
首先让我们防止空 timeZone
,因为它很容易短路,我们会很高兴知道我们稍后有一个有效的。这里有两个选择;您可以使用条件 return None
(Java 样式),或者您可以将整个方法的其余部分包装在 flatMap 中(这是我的偏好)。我们还将在此处检查 ZoneRulesException
位并将无效时区视为 "no time zone"(可以说是):
Option(timeZone).flatMap(tz => Try(ZoneId.of(tz)).toOption) flatMap { incomingTZ =>
// rest of method
}
due date is null, calculate quarterly label from start date
due date starts with year 9999, calculate quarterly label from start date
9999?呸。好的,我们可以很好地处理这两种情况:
def parseDate(s: String) = Try(LocalDateTime.parse(startDate, formatter)).toOption
val validDueDate = Option(dueDate).flatMap(parseDate).filter(_.getYear < 9999)
(从技术上讲,您的规范讨论了以 9999 开头的日期字符串,因此我们应该在解析之前 .filterNot(_ startsWith "9999")
。但是这种方式感觉更清晰,并且可以更轻松地适应其他基于日期的过滤。)
开始日期的处理类似,只是没有年份过滤:
val validStartDate = Option(startDate).flatMap(parseDate)
现在我们有两个输入选项。我认为案例的模式匹配在这里可能是最简单的,以计算出标签的日期:
val dateForCalc = (validStartDate, validDueDate) match {
case (Some(sd), Some(dd)) if sd == dd => sd // arbitrary, could pick either
case (Some(sd), Some(dd)) => throw IllegalArgumentException(s"Start Date $sd != Due Date $dd!")
case (Some(sd), None) => sd
case (None, Some(dd)) => dd
case (None, None) => return None // or whatever "do nothing" means
}
太好了,现在我们在 dateForCalc
中有了开始日期或截止日期,只需要从中计算出季度标签。在这一点上,我们可以只使用所有现有的 if-else 块的内容:
val incomingDate = dateForCalc.atZone(incomingTZ)
val calculatedDate = incomingDate .withZoneSameInstant(calculatorTZ).toLocalDate
val quarter = calculatedDate.get(IsoFields.QUARTER_OF_YEAR)
val year = calculatedDate.getYear
Some(year + " " + quarter)
我认为这是一个相当不错的方法。我们预先进行验证、解析和日期比较;这样,当所有这些都完成时,您就可以使用 相关且经过验证的 输入,只需执行计算季度和年度的无聊逻辑即可。