范围不与其他范围
所以我不得不做出这样的处理以下情况轨验证:范围不与其他范围
我有一组范围的,我需要知道,如果问的范围有任何冲突在这些范围内。
例如,我有以下范围:
- (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
但我真的不能弄清楚如何处理其他情况下以简单的方式。
这是其中一个问题,当以正确的方式查看时很简单,但是否则很麻烦和复杂。在几次错误的开始之后,我提出了这个问题:“如何避免重叠?”
我假设数组中的范围是按照每个范围的结束不大于每个范围的下一个和每个范围的开始都不小于前一个的结束,这就是你的例子,如果这个条件不成立,第一步就是修改数组,使得这个条件成立,不是难事。
代码
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
这是一个很好的答案,最有可能是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..5000
是3000..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
如果被测试的范围是'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更改无效。
这将返回“2001..3000与2010..2020之间的冲突”,范围= 2000..2001。 – 2014-11-04 22:27:24
是的,这是因为2001..3000与2010..2020重叠(在comun中每个愤怒至少有1个元素,那么搜索者寻找什么? – 2014-11-04 22:47:38
“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
我想我已经涵盖了所有可能的情况,但目前还不确定。
你需要通过代码证明这一点?简单的答案是它们确实有冲突,因为像'1..10'这样的两个点的范围包括* 1和10两者,无论是否有像1 ... 10这样的三个点只包括1和* * 10 – 2014-11-04 19:28:31
是的,我必须编写它,它应该是一个Rails验证。 – 2014-11-04 19:29:38
“Rails验证”是什么意思?模型验证?或者像一个证明它的功能?为什么你需要这样的功能? – 2014-11-04 19:31:21