2017-06-15 28 views
1

FROMとTOのペアの日付が大量にあり、その中のすべての日をカウントする必要があります。しかし、2つの範囲が重複している場合、重複する日は2回カウントされません。VB.NETの日付範囲が重複している日数

Dim total_days_used = 0 
For Each row As DataRow In MY_DATA.Tables(0).Rows 
    Dim total_days As Double = 0 

    Dim date_from = MY_FROM_DATE_FROM_DATA 
    Dim date_to = MY_TO_DATE_FROM_DATA 

    Dim span = date_to - date_from 

    total_days = span.TotalDays '=4 
    total_days_used += total_days 
Next 

は私も重なった日を引く、あるいはそれらを追跡する簡単な方法を把握することはできません。

は、ここで私が持っているすべての日を数えるコードです。私はそれが行く一つの方法だろうと思う、もう一つは、重複していない範囲のセットで終わるまで重複範囲を結合することですが、それはあまりにも複雑すぎるようです。これを行う簡単な方法があるはずですか?

+0

重複した日付をリストまたは辞書に入れて、すでに数えられているものを知ることができます – Plutonix

+0

最初に重複のない範囲のリストを取得します。ループし、他のリストの範囲をマージしてオーバーラップします。その後、あなたは日数を得ることができます。 –

答えて

1

何かこれはうまくいくはずです。 最後に電流が重なっているかどうかを知るために範囲を最初に並べます。 そして、重複する日数を数え、合計からそれを引く。 範囲を含めたい場合は+1日、それ以外の場合は削除してください。

Private Sub Main() 
    Dim ranges = New List(Of Range)() From { _ 
     {New Range(New DateTime(2000, 1, 1), New DateTime(2000, 1, 30))}, _ 
     {New Range(New DateTime(2000, 1, 28), New DateTime(2000, 2, 3))} _ 
    } 
    CountNonOverlappingsDays(ranges).Dump() '34 days 
End Sub 

Private Function CountNonOverlappingsDays(ranges As IEnumerable(Of Range)) As Integer  
    Dim isFirst = True 
    Dim last As Range = Nothing 
    Dim overlapping As Integer = 0 
    Dim total As Integer = 0 
    For Each current In ranges.OrderBy(Function(r) r.[To]) 
     total += CInt((current.[To] - current.From).TotalDays) + 1 '+1 if we want Inclusive count 

     If isFirst Then 
      isFirst = False 
      last = current 
      Continue For 
     End If 
     If (last.From <= current.[To]) AndAlso (last.[To] >= current.From) Then 
      Dim start = current.From 
      Dim [end] = last.[To] 
      overlapping += CInt(([end] - start).TotalDays) + 1 '+1 if we want Inclusive count 
     End If 
     last = current 
    Next 
    Return total - overlapping 
End Function 

    Public Class Range 
    Public Sub New([from] As DateTime, [to] As DateTime) 
     [From] = [from] 
     [To] = [to] 
    End Sub 
    Public Property [From]() As DateTime 
     Get 
      Return m_From 
     End Get 
     Set 
      m_From = Value 
     End Set 
    End Property 
    Private m_From As DateTime 
    Public Property [To]() As DateTime 
     Get 
      Return m_To 
     End Get 
     Set 
      m_To = Value 
     End Set 
    End Property 
    Private m_To As DateTime 
End Class 
+0

ありがとうございました。調整することなくほぼそのまま動作させることができました。助けが大いにありがとう! – mmvsbg

0

以下を使用してください(または同様のDateRangeクラス)。

Class DateRange 
    Implements IEnumerable(Of DateTime) 
    Public Sub New(startDate As DateTime, endDate As DateTime) 
     me.StartDate = startDate 
     me.EndDate = endDate 
    End Sub 

    Public ReadOnly Property StartDate() As DateTime 
    Public ReadOnly Property EndDate() As DateTime 

    Public Function GetEnumerator() As IEnumerator(Of DateTime) Implements IEnumerable(of DateTime).GetEnumerator 
     Return Enumerable.Range(0, 1 + EndDate.Subtract(StartDate).Days).[Select](Function(offset) StartDate.AddDays(offset)).GetEnumerator() 
    End Function 

    Private Function IEnumerable_GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator 
     Return GetEnumerator() 
    End Function 
End Class 

このクラスの重要な部分は、列挙可能であることです。つまり、for eachループ(他のものの中で)で使用された場合、開始日と終了日の間の日付のシーケンスを返します(両端を含む)。

次に、あなたが望む結果を得るために、このようなコードを使用することができます:

Dim ranges = New List(Of DateRange)() 
ranges.Add(New DateRange(#2017/1/1#,#2017/1/10#)) 
ranges.Add(New DateRange(#2017/1/8#,#2017/1/20#)) 

Dim merged = ranges.SelectMany(Function(r) r.AsEnumerable()).Distinct().OrderBy(Function(dt) dt) 
Console.WriteLine($"{merged.Count()} days: ") 
For Each [date] As DateTime In merged 
    Console.WriteLine([date].ToShortDateString()) 
Next 
Console.ReadLine() 

これは、内のすべてのDateRangeのインスタンスから(IEnuemrableDateRangeによって作成された)日付のリストを平らにするためにLINQ SelectMany機能を使用していますDateTimeの単一のリストに一覧表示します。次に、別個の(一意の)値を取得し、リストをソートします。

出力には、2つのDateRangeインスタンスを含むリストからの出力が表示されます。 1つは2017年1月1日から2017年1月10日までで、2017年1月8日から2017年1月20日までの2番目のものです。これらの範囲は8,9,10で重複しています。ご覧のとおり、これらの重複した日付は一度だけ含まれています。

次の出力が生成される:

20日:2017年1月1日

2017年1月2日2017年1月3日

2017年1月4日
1/2017分の5
2017年1月6日
2017年1月7日2017年1月8日

2017年1月9日
2017年1月10日2017年1月11日

2017年1月12日2017年1月13日

2017年1月14日2017年1月15日

2017年1月16日
2017年1月17日
2017年1月18日
2017年1月19日
2017年1月20日

0

これを試してみてください。

Dim range As New List(Of DateTime)() 

For Each row As DataRow In MY_DATA.Tables(0).Rows 
    Dim date_from = MY_FROM_DATE_FROM_DATA 
    Dim date_to = MY_TO_DATE_FROM_DATA 
    range.AddRange(Enumerable.Range(0, (date_from-date_to).TotalDays).Select(d => date_from.AddDays(d)) 
Next 
Dim total_days_used As Integer = range.Distinct().Count() 

魔法は2つの部分に分かれています。最初の部分はEnumerable.Range()を使用して実際に各範囲のすべての日付を投影するので、リストに追加することができます。 2番目の部分は、そのリストの別個のメンバーを取り、それらを数えます。

関連する問題