gap and island 解决方案实际上是一段非常短的代码——大部分是输出。我正在使用 Nigel Heffernan 发布的quicksort
Option Base 1
Option Explicit
Sub GapAndIsland()
Dim varData As Variant
Dim minStart As Variant
Dim maxEnd As Variant
Dim i As Long
Dim gap As Variant
' Set the array
varData = Range("f2:g6").Value
' *** Modified code to remove bad data ***
' Remove elements that are not number or date
Call RemoveInvalid(varData, lastUsed)
' Sort https://stackoverflow.com/questions/4873182/sorting-a-multidimensionnal-array-in-vba
Call QuickSortArray(varData, 1, lastUsed, 1)
' *** end of modified code ***
' Initialise
minStart = varData(1, 1)
maxEnd = varData(1, 1)
gap = 0
' Loop over rows
' *** This line also modified ***
For i = 1 To lastUsed
' If there is a gap, increment total gap length
If (varData(i, 1)) > maxEnd Then
gap = gap + varData(i, 1) - maxEnd
End If
' Update latest end time
If varData(i, 2) > maxEnd Then
maxEnd = varData(i, 2)
End If
Next i
' Output
Range("I1:j6").Clear
Range("I1:j1").Font.Bold = True
Range("I1:J1").HorizontalAlignment = xlCenter
Range("j2:j3").NumberFormat = "dd/mm/yyyy hh:mm:ss"
Range("j4:j6").NumberFormat = "[h]:mm:ss"
Range("I1").Value = "Measure"
Range("J1").Value = "Value"
Range("i2").Value = "Start"
Range("j2").Value = minStart
Range("i3").Value = "End"
Range("j3").Value = maxEnd
Range("i4").Value = "Duration"
Range("j4").Value = maxEnd - minStart
Range("i5").Value = "Gaps"
Range("j5").Value = gap
Range("i6").Value = "Net"
Range("j6").Value = maxEnd - minStart - gap
End Sub
这使用与问题中相同的连续测试数据,但格式为日期时间:
这是一个 UDF 版本,它只返回任务的净时间。
Function TimeOnTask(R As Range) As Variant
Dim varData As Variant
Dim minStart As Variant
Dim maxEnd As Variant
Dim i As Long
Dim gap As Variant
' Set the array
varData = R.Value
' *** Modified code to remove bad data ***
' Remove elements that are not number or date
Call RemoveInvalid(varData, lastUsed)
' Sort https://stackoverflow.com/questions/4873182/sorting-a-multidimensionnal-array-in-vba
Call QuickSortArray(varData, 1, lastUsed, 1)
' *** end of modified code ***
' Initialise
minStart = varData(1, 1)
maxEnd = varData(1, 1)
gap = 0
' Loop over rows
' *** This line also modified ***
For i = 1 To lastUsed
' If there is a gap, increment total gap length
If (varData(i, 1)) > maxEnd Then
gap = gap + varData(i, 1) - maxEnd
End If
' Update latest end time
If varData(i, 2) > maxEnd Then
maxEnd = varData(i, 2)
End If
Next i
TimeOnTask = maxEnd - minStart - gap
End Function
** 编辑 **
我试图通过添加一个简短的例程来删除开始时间或停止时间不是数字或日期的行来使其更加健壮(另请参阅上面的修改代码):
Sub RemoveInvalid(ByRef arr As Variant, ByRef lastUsed As Long)
Dim i As Long
Dim j As Long
j = 0
For i = 1 To UBound(arr, 1)
' Increment and copy row if valid
If (IsNumeric(arr(i, 1)) Or IsDate(arr(i, 1))) And Not (IsEmpty(arr(i, 1))) And _
(IsNumeric(arr(i, 2)) Or IsDate(arr(i, 2))) And Not (IsEmpty(arr(i, 2))) Then
j = j + 1
arr(j, 1) = arr(i, 1)
arr(j, 2) = arr(i, 2)
End If
Next i
lastUsed = j
End Sub
没有必要重新调整数组,因为您可以在快速排序调用以及间隙和孤岛循环中指定最后使用的行。