Cho phép full_range
là một dải và ranges
là một dãy các dải, đại diện cho những gì người hỏi gọi là period
và incidents
, tương ứng. Tôi đã giả sử các phần tử chứa trong tất cả các phạm vi có thể là bất kỳ đối tượng nào, miễn là chúng có thể được so sánh với phương thức của lớp hiện hành '<=>
và mô-đun Comparable đã là include
d.
Mã
def uncovered_ranges(full_range, ranges)
ucrs = [full_range]
ranges.each do |r|
ucrs = ucrs.flat_map do |ucr|
if ucr.first >= r.last || ucr.last <= r.first
ucr
elsif r.first <= ucr.first && r.last >= ucr.last
nil
elsif r.first <= ucr.first && r.last < ucr.last
r.last..ucr.last
elsif r.first > ucr.first && r.last >= ucr.last
ucr.first..r.first
else
[ucr.first..r.first, r.last..ucr.last]
end
end.compact
end
ucrs
end
Ví dụ
full_range = 1..20
ranges = [3..4, 6..8, 10..12, 8..14, 16..17, 20..20]
uncovered_ranges(full_range, ranges)
#=> [1..3, 4..6, 14..16, 17..20]
require 'time'
full_range = Time.parse('8:00am')..Time.parse('8:00pm')
#=> 2016-12-22 08:00:00 -0800..2016-12-22 20:00:00 -0800
ranges = [
Time.parse('7:00am')..Time.parse('9:00am'),
Time.parse('1:00pm')..Time.parse('3:00pm'),
Time.parse('1:30pm')..Time.parse('3:30pm'),
Time.parse('7:00pm')..Time.parse('9:00pm'),
]
#=> [2016-12-22 07:00:00 -0800..2016-12-22 09:00:00 -0800,
# 2016-12-22 13:00:00 -0800..2016-12-22 15:00:00 -0800,
# 2016-12-22 13:30:00 -0800..2016-12-22 15:30:00 -0800,
# 2016-12-22 19:00:00 -0800..2016-12-22 21:00:00 -0800]
uncovered_ranges(full_range, ranges)
#=> [2016-12-22 09:00:00 -0800..2016-12-22 13:00:00 -0800,
# 2016-12-22 15:30:00 -0800..2016-12-22 19:00:00 -0800]
Giải thích
Có lẽ cách dễ nhất và hầu hết thông qua cho tôi để giải thích những gì đang xảy ra là để chèn một số puts
báo cáo và chạy mã cho ví dụ đầu tiên ở trên.
def uncovered_ranges(full_range, ranges)
ucrs = [full_range]
puts "ucrs initially=#{ucrs}"
ranges.each do |r|
puts "\ncovering range r=#{r}"
ucrs = ucrs.flat_map do |ucr|
puts " range uncovered so far ucr=#{ucr}"
if ucr.first >= r.last || ucr.last <= r.first
puts " in if #1, returning #{ucr}"
ucr
elsif r.first <= ucr.first && r.last >= ucr.last
puts " in if #2, returning nil"
nil
elsif r.first <= ucr.first && r.last < ucr.last
puts " in if #3, returning #{r.last..ucr.last}"
r.last..ucr.last
elsif r.first > ucr.first && r.last >= ucr.last
puts " in if #4, returning #{ucr.first..r.first}"
ucr.first..r.first
else
puts " in else, returning #{[ucr.first..r.first, r.last..ucr.last]}"
[ucr.first..r.first, r.last..ucr.last]
end
end.tap { |u| puts "ucrs after processing range #{r}=#{u}" }.
compact.
tap { |u| puts "ucrs after compact=#{u}" }
end
ucrs
end
uncovered_ranges 1..20, [3..4, 6..8, 10..12, 8..14, 16..17, 20..20]
in như sau.
ucrs initially=[1..20]
covering range r=3..4
range uncovered so far ucr=1..20
in else, returning [1..3, 4..20]
ucrs after processing range 3..4=[1..3, 4..20]
ucrs after compact=[1..3, 4..20]
covering range r=6..8
range uncovered so far ucr=1..3
in if #1, returning 1..3
range uncovered so far ucr=4..20
in else, returning [4..6, 8..20]
ucrs after processing range 6..8=[1..3, 4..6, 8..20]
ucrs after compact=[1..3, 4..6, 8..20]
covering range r=10..12
range uncovered so far ucr=1..3
in if #1, returning 1..3
range uncovered so far ucr=4..6
in if #1, returning 4..6
range uncovered so far ucr=8..20
in else, returning [8..10, 12..20]
ucrs after processing range 10..12=[1..3, 4..6, 8..10, 12..20]
ucrs after compact=[1..3, 4..6, 8..10, 12..20]
covering range r=8..14
range uncovered so far ucr=1..3
in if #1, returning 1..3
range uncovered so far ucr=4..6
in if #1, returning 4..6
range uncovered so far ucr=8..10
in if #2, returning nil
range uncovered so far ucr=12..20
in if #3, returning 14..20
ucrs after processing range 8..14=[1..3, 4..6, nil, 14..20]
ucrs after compact=[1..3, 4..6, 14..20]
covering range r=16..17
range uncovered so far ucr=1..3
in if #1, returning 1..3
range uncovered so far ucr=4..6
in if #1, returning 4..6
range uncovered so far ucr=14..20
in else, returning [14..16, 17..20]
ucrs after processing range 16..17=[1..3, 4..6, 14..16, 17..20]
ucrs after compact=[1..3, 4..6, 14..16, 17..20]
covering range r=20..20
range uncovered so far ucr=1..3
in if #1, returning 1..3
range uncovered so far ucr=4..6
in if #1, returning 4..6
range uncovered so far ucr=14..16
in if #1, returning 14..16
range uncovered so far ucr=17..20
in if #1, returning 17..20
ucrs after processing range 20..20=[1..3, 4..6, 14..16, 17..20]
ucrs after compact=[1..3, 4..6, 14..16, 17..20]
#=> [1..3, 4..6, 14..16, 17..20]
Nếu đây là một ứng dụng thực tế và bạn đang sử dụng PostgreSQL, bạn nên xem xét sử dụng [chức năng toán học ngày] (https://www.postgresql.org /docs/9.1/static/functions-datetime.html) thông qua truy vấn SQL thay vì xử lý bên trong logic ứng dụng của bạn. – coreyward
Nếu bạn đang xem xét điều này hoàn toàn từ quan điểm phát triển thuật toán, [câu hỏi/câu trả lời này sẽ cung cấp cho bạn một số thức ăn cho consderation] (http://stackoverflow.com/questions/1193477/fast-algorithm-to-quickly-find - phạm vi-một-số-thuộc-trong-một-bộ-của-phạm vi? rq = 1). – coreyward
@coreyward không may là dữ liệu không được lưu trữ trong cơ sở dữ liệu SQL. Tôi cũng không thực sự rõ ràng nếu phiên bản SQL sẽ dễ dàng hơn dựa trên liên kết (nếu tôi đã nhập vào PSQL trước đó). – Stussa