问题描述
我需要一个看起来很简单的算法,但我仍然想不出一种优化的方法来做到这一点。
我有以下json对象:
[
{
"start": "2000-01-01T04:00:00.000Z","end": "2020-01-01T08:00:00.000Z"
},{
"start": "2000-01-01T05:00:00.000Z","end": "2020-01-01T07:00:00.000Z"
}
]
如您所见,第二个对象在第一个对象的范围内。 我需要遍历此数组并返回有冲突的日期。
我的项目目前在红宝石上,但是我只需要一个如何实现该算法的想法,因此,任何高级编程语言都将是很好的。
有什么想法吗?
解决方法
首先,我们可以转换哈希列表以将日期解析为Date
对象:
require 'date'
dates = input.map do |hsh|
hsh.transform_values { |str| Date.parse str }
end
现在,我们可以使用嵌套循环并使用Range#cover?
查找是否存在重复项:
conflicting = dates.select.with_index do |date,idx|
[date[:start],date[:end]].any? do |date_to_compare|
dates.map.with_index.any? do |date2,idx2|
next if idx == idx2 # so we don't compare to self
(date2[:start]..date2[:end]).cover?(date_to_compare)
end
end
end
,
使用日期字段上的BTREE索引将数据放入数据库。让数据库为您完成工作。
让我们说一下下表:
TABLE myDate {
id BIGINT UNSIGNED,date_start DATETIME,date_end DATETIME
}
然后,您要在date_start和date_end上使用BTREE(或BTREE +)索引,并在id上使用HASH索引。
这些位置到位后,将数据提供给表,并执行以下select语句以查找重叠的时间:
-- Query to select dates that are fully contained such as in the example (l contains r):
SELECT l.id,l.date_start,l.date_end,r.id,r.date_start,r.date_end
FROM myDate l JOIN myDate r ON (l.date_start < r.date_start) AND (l.date_end > r.date_end);
-- Query to select dates that overlap on one side:
SELECT l.id,r.date_end
FROM myDate l JOIN myDate r ON ((l.date_start < r.date_start) AND (l.date_end > r.date_start)) OR ((l.date_start > r.date_start) AND (l.date_end < r.date_start));
,
检测范围覆盖的DateTime对象
也许有一种更优雅的方式来执行此操作,但是对我来说,这似乎相对简单。技巧是将哈希值转换为可以利用内置DateTime方法的优势的Range#cover?范围。
请考虑以下内容:
require 'date'
dates = [
{:start=>"2000-01-01T04:00:00.000Z",:end=>"2020-01-01T08:00:00.000Z"},{:start=>"2000-01-01T05:00:00.000Z",:end=>"2020-01-01T07:00:00.000Z"},]
# convert your date hashes into an array of date ranges
date_ranges = dates.map { |hash| hash.values}.map do |array|
(DateTime.parse(array.first) .. DateTime.parse(array.last))
end
# compare sets of dates; report when the first covers the second range
date_ranges.each_slice(2) do |range1,range2|
puts "#{range1} covers #{range2}" if range1.cover? range2
end
因为Range#cover?如果是Boolean,则您可能更喜欢简单地存储包含的日期并在以后处理它们,而不是立即对每个日期执行操作。在这种情况下,只需使用Array#select。例如:
date_ranges.each_slice(2).select { |r1,r2| r1.cover? r2 }
,
这些字符串看起来像ISO 8601格式。您应该能够轻松地将其解析为Date / DateTime /或类似的对象。检查有关这些类的文档,它将显示在此处,显示cn做到这一点。然后,在解析为对象之后,您应该能够简单地使用 = />运算符比较那些日期对象。这样,您就可以比较开始/结束时间,并且可以确定日期X是否为:
(a)完全在另一个之前
(b)在另一个之前开始,并在另一个之内结束
(c)完全在另一方之内
(d)从头开始,在另一头之后结束
(e)完全追随另一个
(f)更长,并且完全包含另一个
我认为这是所有可能的方法,但是您最好再次检查一下。如果需要,将它们全部绘制在时间轴上,看看是否还有其他可能性。
当您拥有可以进行此分类的代码时,最好去实现基于此的其余逻辑。
但我仍然想不出一种优化的方法
不。首先以任何方式编写它,以使其正常工作和可靠。从头到尾彻底理解问题。然后测量其速度和质量。如果不好,请根据关于速度/质量观察的所有猜测编写一个v2版本。测量和比较。如果仍然不好,请收集代码,数据集,度量,确保没有计算机,网络和密码等的读者可以重复测试用例和度量,然后解释问题以及如何解决/优化。在没有所有这些的情况下,询问“优化” *)通常会导致纯粹的猜测。
*)OFC假设“优化方式”不是一个空话,而是真正的问题表现