So I have a DataTable
that returns results within a month to month range when available. However, when I format the return, I need to still show skipped months as blanks.
I have a working solution, but it's a little verbose and I think it can be done cleaner.
Here's a setup to the problem
Dim startMonth As Date = #5/1/2011#
Dim endMonth As Date = #6/1/2012#
Dim dr As DataRow
Dim dtData As New DataTable
With dtData.Columns
.Add("MonthDate", GetType(Date))
.Add("OtherInfo", GetType(String))
'other columns...
End With
With dtData.Rows
.Add(#6/1/2011#, "Info")
.Add(#7/1/2011#, "Info")
.Add(#7/1/2011#, "Info")
.Add(#1/1/2012#, "Info")
End With
This part will loop through the date ranges and generate a DataTable
with one blank row per possible date
'get all dates
Dim dtAllTimes As DataTable = dtData.Clone()
Do While startMonth <= endMonth
dr = dtAllTimes.NewRow()
dr("MonthDate") = startMonth
dtAllTimes.Rows.Add(dr)
startMonth = startMonth.AddMonths(1)
Loop
This part will create a blank DataTable
with one date per found value
'get used dates
Dim dtUsedTimes As DataTable = dtData.Clone()
For Each usedDate As Date In
From row As DataRow In dtData.AsEnumerable Select CDate(row("MonthDate")) Distinct
dr = dtUsedTimes.NewRow()
dr("MonthDate") = usedDate
dtUsedTimes.Rows.Add(dr)
Next
Finally I combine those two tables to get the unused dates and add them onto the original data
'subtract used dates from all dates
Dim unusedDates As IEnumerable(Of DataRow)
unusedDates = dtAllTimes.AsEnumerable.Except(dtUsedTimes.AsEnumerable(), DataRowComparer.Default)
'add subtraction back into all
dtData = dtData.AsEnumerable.Union(unusedDates).CopyToDataTable()
Any suggestions would be great.
Update...
Implementing my own custom comparison class helps to shave off a couple lines of code and processing time by not having to compare every column in the entire row against every other column and also helps skip the step of finding used rows since that was only done to put them into a DataTable that could be used with the Default DataRowComparer
.
Here's the custom DataRowComparer
Public Class DataRowDateComparer : Implements IEqualityComparer(Of DataRow)
Public Function Equals(x As DataRow, y As DataRow) As Boolean _
Implements IEqualityComparer(Of DataRow).Equals
Return x("MonthDate") = y("MonthDate")
End Function
Public Function GetHashCode(obj As DataRow) As Integer _
Implements IEqualityComparer(Of DataRow).GetHashCode
Return obj.ToString.GetHashCode
End Function
End Class
Here's the code after creating a DataTable with every date
'only add rows that aren't already in datatable
dtData = dtData.AsEnumerable _
.Union(dtAllTimes.AsEnumerable, New DataRowDateComparer) _
.CopyToDataTable()
Also, sorting by MonthDate in a DataView
can help view the results more easily
Dim dv As New DataView(dtData)
dv.Sort = "MonthDate"