2017-01-13 7 views
0

は、日付範囲の配列を考えます配列内の要素はgiven_date_range以内ですか?この場合の
、それは実際の値でオーバーラップ

[[date_range1.end + 1, date_range2.begin - 1], 
[date_range2.end + 1, date_between_date_range2_and_date_range3.end]] 

例を返さなければならない:

given_date_range = (Date.new(2014, 7, 3) .. Date.new(2016, 3, 18)) 


array_of_date_ranges = [(Date.new(2014, 5, 10) .. Date.new(2014, 8, 10)), 
         (Date.new(2015, 3, 2) .. Date.new(2015, 4, 9)), 
         (Date.new(2016, 3, 5) .. Date.new(2016, 4, 8)), 
         (Date.new(2016, 6, 2) .. Date.new(2016, 7, 3)), 
              ...     ] 

期待される結果:

[[Date.new(2014, 8, 11), Date.new(2015, 3, 1)], 
[Date.new(2015, 4, 10), Date.new(2016, 3, 4)]] 
+0

にそれらの範囲を変換します。それは範囲オブジェクトですか?または配列ですか? – sawa

+0

なぜdate_between_date_range2_and_date_range3なのですか?元の配列になかったときの結果に「終了」しますか? – ndn

+0

range2にrange3が含まれているとどうなりますか?実際の値を使って例を挙げられますか? – ndn

答えて

1

配列操作

範囲はドンので

」あまりにも多くの要素がある場合、それらを日付配列に変換します。

given_date_rangeからすべてのdate_rangesの日付を削除して、カバーされていない日付の配列を取得します。私たちは、非連続した日に、この配列をスライスし、日付範囲の配列に日付の配列の結果の配列を変換します

require 'date' 

date_ranges = [(Date.new(2014, 5, 10) .. Date.new(2014, 8, 10)), 
       (Date.new(2015, 3, 2) .. Date.new(2015, 4, 9)), 
       (Date.new(2016, 3, 5) .. Date.new(2016, 4, 8)), 
       (Date.new(2016, 6, 2) .. Date.new(2016, 7, 3))] 

given_date_range = (Date.new(2014, 7, 3) .. Date.new(2016, 3, 18)) 

uncovered_dates = given_date_range.to_a - date_ranges.flat_map(&:to_a) 
puts uncovered_dates.sort 
        .slice_when { |d1, d2| d1 + 1 != d2 } 
        .map { |free_range| (free_range.first..free_range.last) } 
#=> 
# 2014-08-11..2015-03-01 
# 2015-04-10..2016-03-04 

宝石レンジ操作のために

あなたは算術演算を追加するには、このgemを使用することができます範囲で。

完全な範囲から始まり、すべての範囲を1つずつ引きます。

別の範囲をsubstractingは、2つの範囲になることがあるので、スクリプトが実際に[complete_range]で始まり、反復間の範囲の配列を保持します:

require 'date' 
require 'range_operators' 

date_ranges = [(Date.new(2014, 5, 10) .. Date.new(2014, 8, 10)), 
       (Date.new(2015, 3, 2) .. Date.new(2015, 4, 9)), 
       (Date.new(2016, 3, 5) .. Date.new(2016, 4, 8)), 
       (Date.new(2016, 6, 2) .. Date.new(2016, 7, 3))] 

#given_date_range = date_ranges.first.min .. date_ranges.last.max # assuming the ranges are sorted 
given_date_range = (Date.new(2014, 7, 3) .. Date.new(2016, 3, 18)) 

uncovered = date_ranges.inject([given_date_range]) do |free_ranges, range| 
    free_ranges.flat_map do |free_range| 
    free_range - range 
    end 
end 

puts uncovered 
# => 2014-08-11..2015-03-01 
# 2015-04-10..2016-03-04 
1
require "date" 

(array_of_date_ranges.flat_map(&:to_a) & given_date_range.to_a) 
.uniq.sort.chunk_while{|x, y| x.next == y} 
.flat_map{|x, *, y| [x, y]}[1...-1] 
.each_slice(2) 
.map{|x, y| [x + 1, y - 1]} 
# => [ 
    [#<Date: 2014-08-11 ((2456881j,0s,0n),+0s,2299161j)>, #<Date: 2015-03-01 ((2457083j,0s,0n),+0s,2299161j)>], 
    [#<Date: 2015-04-10 ((2457123j,0s,0n),+0s,2299161j)>, #<Date: 2016-03-04 ((2457452j,0s,0n),+0s,2299161j)>] 
] 
+0

Arrayのアミノ酸を使用しても構いません。範囲を持っていた最後の例は、何百万という要素を持っていたので、遅かったでしょう。あなたの解決策では、 'Array#&'の代わりに 'Array# - 'を使用すると、コードが大幅に単純化されます。 AFAIK、 'Array#&'はSetオペレーションなので、 'uniq'はおそらく減少します。 –

+0

このメソッドは 'given_date_range =(Date.new(2014、4、10).. Date.new(2014,6,10)) 'では動作しません。 – Sajjad

0

EDIT:質問の変更を反映するために更新の回答。

これまで私が考えていたのは、これを最も読みやすい方法で解決しました。 私は日付は使用しませんでしたが、整数も同じです。ここで

# helper function 
def discover_extras(two_ranges, given_range) 
    first = two_ranges.first 
    last = two_ranges.last 

    if given_range.cover?(first.end) 
    if given_range.cover?(last.begin) 
     first.end+1..last.begin-1 
    else 
     first.end+1..given_range.end if given_range.end > first.end 
    end 
    else 
    nil 
    end 
end 

def values_not_covered_in(ranges, given_range) 
    not_covered = [] 

    ranges.each_cons(2) do |two_ranges| 
    extras = discover_extras(two_ranges, given_range) 
    not_covered.push(extras) if extras 
    end 

    not_covered 
end 

は、私はそれをすべて

require "dates_not_covered" 

describe '#discover_extras(two_ranges, given_range)' do 
    it 'when before range returns nil' do 
    expect(discover_extras([2..10, 12..15], 0..1)).to be nil 
    end 

    it 'when covers first range returns nil' do 
    expect(discover_extras([2..10, 12..15], 2..10)).to be nil 
    end 


    it 'when includes both ranges returns between ranges' do 
    expect(discover_extras([2..10, 13..15], 2..15)).to eq(11..12) 
    end 

    it 'for [2..10, 12..15] and 2..11 returns 11..11' do 
    expect(discover_extras([2..10, 12..15], 2..11)).to eq(11..11) 
    end 
end 

describe '#values_not_covered_in(ranges, given_range)' do 
    it 'for [2..10, 12..15] and 2..10 returns []' do 
    expect(values_not_covered_in([2..10, 12..15], 2..10)).to eq [] 
    end 

    it 'for [2..10, 12..15] and 2..11 returns [11..11]' do 
    expect(values_not_covered_in([2..10, 12..15], 2..11)).to eq([11..11]) 
    end 

    it 'for [2..10, 12..15] and 2..15 returns [11..11]' do 
    expect(values_not_covered_in([2..10, 12..15], 2..15)).to eq([11..11]) 
    end 

end 

もう一度、これはint型と日付の両方のために働くことを確認するために使用さspecファイルです。 違いは、範囲の配列を返すことを決めたことです。範囲の開始と終了を持つ配列ではありません。私はそれがこのように良いと思う。

あなたは、配列の配列を返すことを主張した場合、その後、ちょうどあなたが日付範囲によって何を意味するかは明らかではない配列

not_covered.push([extras.begin, extras.eng]) if extras 
+0

テストの作成は良いです。優れている。メソッドの出力する範囲が正しくありません。 –

+0

私がこの作業を始めたとき、彼が何を望んでいるかは本当に明確ではありませんでした。どのように出力が正しくないのですか? –

+0

更新された質問には1つの例があります。 –

関連する問題