如何检测并发日期

问题描述

我需要一个看起来很简单的算法,但我仍然想不出一种优化的方法来做到这一点。

我有以下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假设“优化方式”不是一个空话,而是真正的问题表现