范围不与其他范围

问题描述:

冲突

所以我不得不做出这样的处理以下情况轨验证:范围不与其他范围

我有一组范围的,我需要知道,如果问的范围有任何冲突在这些范围内。

例如,我有以下范围:

  • (0..3000)
  • (3000..4000)
  • (4000..5000)
  • (6000。 .7000)

    • 1..10会发生冲突,因为这个范围已经包含在0..3000

    • 1..3100将是冲突的,因为被包括在该范围,部分,0..3000和3000.4000 2800..4500之间

    • 将是冲突的,因为包括该范围内,部分地之间0..3000,3000..4000和4000..5000

    • 5000..6000不会在冲突

我已经实现的唯一的事情是第一次和最简单的情况机智h该行:

def is_not_conflictive_range? 
    ranges = ServicePrice.where(property: self.property).pluck(:from_value, :to_value).map { |range| range.first..range.second } 
    conflictive_range = ranges.find do |range| 
    range.include? (self.from_value..self.to_value) 
    end 
    errors.add(:service_price, "range from #{self.from_value} to #{self.to_value} is including in existing range from #{conflictive_range.first} to #{conflictive_range.last}") if conflictive_range 
end 

但我真的不能弄清楚如何处理其他情况下以简单的方式。

+0

你需要通过代码证明这一点?简单的答案是它们确实有冲突,因为像'1..10'这样的两个点的范围包括* 1和10两者,无论是否有像1 ... 10这样的三个点只包括1和* * 10 – 2014-11-04 19:28:31

+0

是的,我必须编写它,它应该是一个Rails验证。 – 2014-11-04 19:29:38

+0

“Rails验证”是什么意思?模型验证?或者像一个证明它的功能?为什么你需要这样的功能? – 2014-11-04 19:31:21

这是其中一个问题,当以正确的方式查看时很简单,但是否则很麻烦和复杂。在几次错误的开始之后,我提出了这个问题:“如何避免重叠?”

我假设数组中的范围是按照每个范围的结束不大于每个范围的下一个和每个范围的开始都不小于前一个的结束,这就是你的例子,如果这个条件不成立,第一步就是修改数组,使得这个条件成立,不是难事。

代码

def no_overlap?(arr, range) 
    range.last <= arr.first.first || 
    range.first >= arr.last.last || 
    arr.each_cons(2).any? { |r1,r2| 
    range.first >= r1.last && range.last <= r2.first } 
end 

个例子

arr = [1000..3000, 3000..4000, 4000..4000, 4000..5000, 6000..7000] 

no_overlap?(arr, 1..1010) #=> false 
no_overlap?(arr, 2800..4500) #=> false 
no_overlap?(arr, 2500..5500) #=> false 
no_overlap?(arr, 5000..6000) #=> true 
no_overlap?(arr, 0..500) #=> true 
no_overlap?(arr, 8000..9000) #=> true 
+0

这是一个很好的答案,最有可能是Matías想要的。 +1我仍然不确定他是否希望'5000..6000'返回* true *或* false *。正常范围会冲突... – 2014-11-05 08:45:22

首先,我会重命名功能,更具可读性。我选择了valid_range

你来相当接近你的代码,我的最终结果是:

def valid_range? 
    ranges = ServicePrice.where(property: self.property).pluck(:from_value, :to_value).map { |range| range.first..range.second } 

    ranges.each do |range| 
    # check if either values are part of the range 
    if range.include?(from_value) || range.include?(to_value) 
     # invalid, so return false 
     return false 
    end 
    end 

    # if it 'survived' the loop return true 
    return true 
end 

所以它遍历所有现有的循环和上一个错误返回false。

尽管如此,由于4000..50003000..4000范围的一部分,因此您目前现有的范围无效。

解决方法是使用带三点的范围代替。所以map部分调整到

.map { |range| range.first...range.second } 

您需要检查:

  • 如果包含在任何范围的范围内的第一个数字;
  • 如果范围的最后一个数字包含在任何范围内,
  • 如果任何范围本身包含在所选范围内。

所以,你可以更改您的代码:

conflictive_range = ranges.find_all do |range| 
    range.include?(self.from_value) || 
    range.include?(self.to_value) || 
    (range.first > self.from_value && range.last < self.to_value) 
end 

正如我评论5000..6000会发生冲突,因为有一个范围与5000和其它与6000

+0

如果被测试的范围是'7000..8000',这大概会返回'6000..7000',但是当被测试范围的端点是一个端点时它不是冲突阵列的一个范围。 – 2014-11-04 22:52:57

我用普通的红宝石做这个。 (编辑:感谢Cary Swoveland的帮助)

首先,定义冲突?方法,该方法显示,如果2米范围overlaped(这可以在模型级别上做)

class Range 
    def conflict?(other) 
    self.begin < other.end && self.end > other.begin 
    end 
end 

然后我定义返回true的invalid_range方法如果范围是为设定的无效:

def invalid_range(range, set) 
    set.any? {|s| s.conflict?(range)} 
end 

验证用户数据:

set = [(0..3000), (3000..4000), (4000..5000), (6000..7000)] 

cases = [(1..10), (1..3100), (2800..4500), (5000..6000)] 

# verifying: 
cases.each {|c| puts "testing range: #{c} on set result: #{invalid_range(c,set)}"} 

cases.each的输出...

testing range: 1..10 on set result: true 
testing range: 1..3100 on set result: true 
testing range: 2800..4500 on set result: true 
testing range: 5000..6000 on set result: false 

如果你喜欢这种方法,我认为你可以修改你的模型来做同样的事情。如果您遇到问题,请发布您的模型,我会帮助您。

我希望它有帮助。

已修改:因为它不是AR兼容的,所以对invalid_range更改无效。

+0

这将返回“2001..3000与2010..2020之间的冲突”,范围= 2000..2001。 – 2014-11-04 22:27:24

+0

是的,这是因为2001..3000与2010..2020重叠(在comun中每个愤怒至少有1个元素,那么搜索者寻找什么? – 2014-11-04 22:47:38

+1

“2001..3000”和“2010..2020” '至少有一个(内部)元素是不相关的。问题是给定范围是否具有:a)至少有一个非端点元素在数组中至少有一个范围内;或b)数组中至少有一个范围内的端点,而不是该数组的端点。如果任一条件成立,则给定范围会有冲突。 – 2014-11-04 23:07:33

我们不需要检查Ruby中包含的范围,只需检查边界。

def valid_range? 
    ranges = ServicePrice.where(property: self.property).pluck(:from_value, :to_value).map { |range| [range.first, range.second] } 

    conflict = nil 
    conflict = ranges.map do |min, max| 
    [ 
     self.from_value > min && self.from_value < max, 
     self.to_value > min && self.to_value < max 
    ].any? 
    end.index(true) 

    errors.add(:service_price, "range from #{self.from_value} to #{self.to_value} is including in existing range from #{ranges[conflict].first} to #{ranges[conflict].last}") unless conflict.blank? 
end 

我想我已经涵盖了所有可能的情况,但目前还不确定。