我想知道在scala中解决这个问题的惯用方法。
给定开始日期和结束日期,以及介于两者之间的日期集合,确定给定的日期集合是否包含从开始日期到结束日期的所有必需日期,并且两个日期之间没有间隔日期。
类型签名:
def checkDate(start: DateTime, end: DateTime, between: IndexedSeq[DateTime]): Boolean
要做到这一点,“正常”或“非功能性”的方法应该是这样的:
def checkDate(start: DateTime, end: DateTime, between: IndexedSeq[DateTime]): Boolean = {
i = 1
status = true
while(start != end) {
d = start.plusDays(i)
if (!between.contains(d) {
status = false
break
}
i += 1
}
return status
}我如何使用折叠键来做这件事?
这是我到目前为止的思考过程:
def checkDate(start: DateTime, end: DateTime, between: IndexedSeq[DateTime]): Boolean = {
// A fold will assume the dates are in order and move left (or right)
// This means the dates must be sorted.
val sorted = between.sortBy(_.getMillis())
val a = sorted.foldLeft(List[Boolean]) {
(acc, current) => {
// How do I access an iterable version of the start date?
if (current == ??) {
acc :: true
} else false
}
}
// If the foldLeft produced any values that could NOT be matched
// to the between list, then the start date does not have an
// uninterrupted path to the end date.
if (a.count(_ == false) > 0) false
else true
}我只需要弄清楚如何索引start参数,这样我就可以在fold迭代between集合时增加它的值。也有可能fold根本不是我应该使用的。
任何帮助都将不胜感激!
发布于 2017-10-20 12:51:30
您可以在累加器中传递前一个DateTime项目:
val a = sortedBetween.foldLeft((List[Boolean](), start)) {
case ((results, prev), current) => {
... calculate res here ...
(results ++ List(res), current)
}
}但是对于这种检查,你最好使用sliding和forall组合:
sortedBetween.sliding(2).forall {
case List(prev,cur) => ..do the check here ..
}另外,请注意,由于IndexedSeq是不可变的,所以您会忽略between排序结果。修复-使用另一个val:
val sortedBetween = between.sortBy(_.getMillis())发布于 2017-10-20 13:56:51
我认为折叠是不必要的,它使事情变得太难了。
假设您有以下函数:
private def normalizeDateTime( dt : DateTime ) : DateMidnight = ???
private def requiredBetweens( start : DateMidnight, end : DateMidnight ) : Seq[DateMidnight] = ???然后你可以像下面这样写你的函数:
def checkDate(start: DateTime, end: DateTime, between: IndexedSeq[DateTime]): Boolean = {
val startDay = normalizeDateTime( start )
val endDay = normalizeDateTime( end )
val available = between.map( normalizeDateTime ).toSet
val required = requiredBetweens( startDay, endDay ).toSet
val unavailable = (required -- available)
unavailable.isEmpty
}请注意,此函数不要求中间元素的顺序,将元素视为一个集合,只要求每天都在某个地方可用。
要实现normalizeDateTime(...),您可能会使用像dt.toDateMidnight这样简单的东西,但您应该考虑一下Chronology和时区问题。您想要表示某一天的DateTime对象始终规范化为相同的DateMidnight,这一点很重要。
要实现requiredBetweens(...),您可以考虑使用Stream和takeWhile(...)作为一个优雅的解决方案。您可能需要该(end isAfter start)。
发布于 2017-10-20 14:05:22
我会使用过滤器,然后压缩并取差值,日期应该总是相隔一天,所以检查它们都是1。
@ val ls = Array(1, 2, 3, 4, 5, 6, 7) // can use dates in the same way
ls: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7)
@ val ls2 = ls.filter { i => (2 < i) && (i < 6) }
ls2: Array[Int] = Array(3, 4, 5)
@ ls2.zip(ls2.drop(1))
res21: Array[(Int, Int)] = Array((3, 4), (4, 5))
@ ls2.zip(ls2.drop(1)).map { case (x, y) => y-x }
res22: Array[Int] = Array(1, 1)
@ ls2.zip(ls2.drop(1)).map { case (x, y) => y-x }.forall { _ == 1 }
res23: Boolean = true您还必须检查是否没有遗漏日期:
@ ls2.length == 6 - 2 - 1 // beware off-by-one errors
res25: Boolean = true您也可以通过使用Range对象更简单地完成此操作:
@ ls2.zipAll(3 to 5 by 1, 0, 0).forall { case (x, y) => x == y }
res46: Boolean = true这应该可以工作,但可能需要对DateTime稍作调整……
@ val today = LocalDate.now
today: LocalDate = 2017-10-19
@ val a = (0 to 9).reverse.map { today.minusDays(_) }
a: collection.immutable.IndexedSeq[LocalDate] = Vector(2017-10-10, 2017-10-11, 2017-10-12, 2017-10-13, 2017-10-14, 2017-10-15, 2017-10-16, 2017-10-17, 2017-10-18, 2017-10-19)
@ a.zip(a.drop(1)).map { case (x, y) => x.until(y) }.forall { _ == Period.ofDays(1) }
res71: Boolean = truehttps://stackoverflow.com/questions/46841897
复制相似问题