计算具有多个其他范围的矩范围的覆盖率
Counting coverage of moment-range with multiple other ranges
我有一组 moment-range 个对象:
a = range(2018-10-01 -> 2018-10-05)
b = range(2018-10-10 -> 2018-10-15)
c = range(2018-10-15 -> 2018-10-20)
d = range(2018-10-21 -> 2018-10-25)
所以我们有一些 adjacent ranges, but none that overlap。我还有一个主要范围:
primary = range(2018-10-01 -> 2018-10-31)
最终目标是创建一些函数来检查使用其他范围的主要范围的覆盖范围:
coverage(primary, [a,b,c,d])
// returns 21
// (5 days + 6 days + 5 days + 5 days)
我知道范围是时间 (ms) 而不是基于天,但理想情况下我稍后会在我的函数中的某个地方转换成那个。
请注意,它不会重复计算 2018-10-15
。为此,我考虑的主要解决方案是遍历每个范围,如果它们相邻或相邻,则使用 add 方法和 {adjacent:true}
将范围合并为最大可能的块。这仍然可能给我留下一些空白,并且需要用蛮力找到主要范围内的总天数(遍历 primary
的每一天,看看它是否存在于任何次要范围内.
另一个棘手的部分是,如果其中一个次要范围 starts/ends 在主要范围之外(可能 2018-09-25 -> 2018-10-02
)。
我是否遗漏了一些可以帮助我实现这一目标的记录方法?也许 contains or within?
的一些巧妙用法
谢谢
编辑:This discussion 似乎切中要害,我会在试验了他们的 subtract
示例以查看它是否适用于此处后报告。
此算法适用于您发布的示例:
function subtractRanges (source, others) {
if (!Array.isArray(source)) {
source = [source]
}
return flatten(source.map(s => {
return flatten(others).reduce((remaining, o) => {
return flatten(remaining.map(r => r.subtract(o)))
}, [s])
return remaining
}))
}
这是一个使用您在问题中发布的日期的示例:
https://runkit.com/rocketraman/5b1aaf0bc454290012b9fc5a
结果是:
2018-10-05T00:00:00-04:00/2018-10-10T00:00:00-04:00
2018-10-20T00:00:00-04:00/2018-10-21T00:00:00-04:00
2018-10-25T00:00:00-04:00/2018-10-31T00:00:00-04:00
这正是我所期望的。
注意:我知道您正在处理日期而不是时间。如果您的输入日期是具有 0 时间和 utc()
的时刻值,请注意时刻范围内有 another issue 会丢失输入的时区信息,这可能会影响您的日期结果。您可以通过使用如下函数映射结果来解决它:
function fixRangeIssue228 (range) {
// workaround https://github.com/rotaready/moment-range/issues/228
return moment.range(range.start.utc(), range.end.utc())
}
即
let result = subtractRanges(...).map(fixRangeIssue228)
我有一组 moment-range 个对象:
a = range(2018-10-01 -> 2018-10-05)
b = range(2018-10-10 -> 2018-10-15)
c = range(2018-10-15 -> 2018-10-20)
d = range(2018-10-21 -> 2018-10-25)
所以我们有一些 adjacent ranges, but none that overlap。我还有一个主要范围:
primary = range(2018-10-01 -> 2018-10-31)
最终目标是创建一些函数来检查使用其他范围的主要范围的覆盖范围:
coverage(primary, [a,b,c,d])
// returns 21
// (5 days + 6 days + 5 days + 5 days)
我知道范围是时间 (ms) 而不是基于天,但理想情况下我稍后会在我的函数中的某个地方转换成那个。
请注意,它不会重复计算 2018-10-15
。为此,我考虑的主要解决方案是遍历每个范围,如果它们相邻或相邻,则使用 add 方法和 {adjacent:true}
将范围合并为最大可能的块。这仍然可能给我留下一些空白,并且需要用蛮力找到主要范围内的总天数(遍历 primary
的每一天,看看它是否存在于任何次要范围内.
另一个棘手的部分是,如果其中一个次要范围 starts/ends 在主要范围之外(可能 2018-09-25 -> 2018-10-02
)。
我是否遗漏了一些可以帮助我实现这一目标的记录方法?也许 contains or within?
的一些巧妙用法谢谢
编辑:This discussion 似乎切中要害,我会在试验了他们的 subtract
示例以查看它是否适用于此处后报告。
此算法适用于您发布的示例:
function subtractRanges (source, others) {
if (!Array.isArray(source)) {
source = [source]
}
return flatten(source.map(s => {
return flatten(others).reduce((remaining, o) => {
return flatten(remaining.map(r => r.subtract(o)))
}, [s])
return remaining
}))
}
这是一个使用您在问题中发布的日期的示例:
https://runkit.com/rocketraman/5b1aaf0bc454290012b9fc5a
结果是:
2018-10-05T00:00:00-04:00/2018-10-10T00:00:00-04:00
2018-10-20T00:00:00-04:00/2018-10-21T00:00:00-04:00
2018-10-25T00:00:00-04:00/2018-10-31T00:00:00-04:00
这正是我所期望的。
注意:我知道您正在处理日期而不是时间。如果您的输入日期是具有 0 时间和 utc()
的时刻值,请注意时刻范围内有 another issue 会丢失输入的时区信息,这可能会影响您的日期结果。您可以通过使用如下函数映射结果来解决它:
function fixRangeIssue228 (range) {
// workaround https://github.com/rotaready/moment-range/issues/228
return moment.range(range.start.utc(), range.end.utc())
}
即
let result = subtractRanges(...).map(fixRangeIssue228)