So I have 2 datasets, one with a time step of 1 minute, the other with a time step of fifteen minutes. I want to convert the 15min data set to the 1min data set by copying the values so that if 4:15 = x1 in the first set, 4:01 to 4:15 = X1 in the second data set.
I wrote the following code, which works fine. However, once past the first values the program slows down to a snail's pace, and takes multiple hours to complete the run.
So my question is: how can I improve the code I wrote so as to achieve completion quicker?
Example data:
Sub AddData()
Dim Query As Worksheet
Dim Time As String
Dim Time2 As String
Dim QueryRangeStart As Range
Dim QueryRangeFinish As Range
Dim QueryRange As Range
Dim Position As Range
Dim Position2 As Range
Set Query = Worksheets("Sheet1") 'Not needed in example, I conserved it so as not to have to rewrite my code
For Each Position In Range(Range("start"), Range("start").End(xlDown)).Cells 'Start is the first cell containing a timestamp in the 15min dataset
Time = Position.Text
Time2 = Position.Offset(1, 0).Text
Set QueryRangeStart = Nothing
Set QueryRangeFinish = Nothing
For Each Position2 In Range(Query.Range("A2"), Query.Range("A2").End(xlDown)).Cells
If InStr(1, Time, Position2.Text, 1) = 1 And QueryRangeStart Is Nothing Then
Set QueryRangeStart = Range(Position2.Address(rowabsolute:=False, columnabsolute:=False, external:=True))
End If
If InStr(1, Time2, Position2.Text, 1) = 1 And QueryRangeFinish Is Nothing Then
Test = False
Set QueryRangeFinish = Range(Position2.Offset(-1, 0).Address(rowabsolute:=False, columnabsolute:=False, external:=True))
Exit For
End If
Next
For Each Position2 In Range(QueryRangeStart, QueryRangeFinish).Cells
Position2.End(xlToRight).Offset(0, 1).Value = Position.Offset(0, 1).Value
Next
Next
End Sub
I had trouble following your code since I wasn't sure which columns your data was in, so I built a new procedure:
Sub split15to1()
Const colIn = 8 'column# where the input 15-mintue intervals are located (data next to it)
Const colOut = 11 'column# where the output 1-minute intervals should go (data next to it)
Const rwStart = 3 'row# where "everything" begins
Dim rwIn As Long, rwOut As Long, x As Long
rwIn = rwStart: rwOut = rwStart 'set start rows
Do
Debug.Print "Splitting: " & Cells(rwIn, colIn) & " : ";
For x = 0 To 14
Debug.Print x & ",";
Cells(rwOut, colOut) = Cells(rwIn, colIn) + TimeSerial(0, x, 0) 'add [x]mins to input time
Cells(rwOut, colOut + 1) = Cells(rwIn, colIn + 1) 'copy value from col next to input time
rwOut = rwOut + 1 'next output row
Next x
Debug.Print "Done."
rwIn = rwIn + 1 'next input row
Loop Until Cells(rwIn, colIn) = "" 'is there data on the next row?
MsgBox "Finished!"
End Sub
Example output:
Alternative
Slightly modified: No output time; place value next to existing data
Option Explicit
Sub split15to1()
Const colIn = 8 'column# where the input 15-mintue intervals are located (data next to it)
Const colOut = 4 'column# where the output 1-minute intervals should go (data next to it)
Const rwStart = 3 'row# where "everything" begins
Dim rwIn As Long, rwOut As Long, x As Long
rwIn = rwStart: rwOut = rwStart 'set start rows
Do
Debug.Print "Splitting: " & Cells(rwIn, colIn) & " : ";
For x = 0 To 14
Debug.Print x & ",";
Cells(rwOut, colOut) = Cells(rwIn, colIn + 1) 'put input data at output location
rwOut = rwOut + 1 'next output row
Next x
Debug.Print "Done."
rwIn = rwIn + 1 'next input row
Loop Until Cells(rwIn, colIn) = "" 'is there data on the next row?
MsgBox "Finished!"
End Sub
In case you're not aware the Debug.Print comments are for troubleshooting and print in the Immediate Window which you can open with CTRL+G from VBA.
If you're not using them, those lines can be deleted or ignored.
Related
I have a nested for loop that first runs through 10-15k rows, compares a cell in that row to another table that is 40k+ rows, if it finds a match, it returns that match, otherwise "no record" is written in a cell. the code works fine, just investigating an alternative approach to make it run faster. currently, 13000 lines takes about 50 min to an hour to run. I've looked into arrays, but loading an array with 40k+ items seems like the wrong route to take. the report is often run bits at a time, so when it is first created it may have 2k rows, then 3k rows may be added to it later, the code below will skip over rows it has already checked and pick up where it left off. any help is appreciated
For i = 2 To lastRow
If Cells(i, 83).Value <> "" Then GoTo NextIteration:
Sheets("mft Rpt").Cells(i, 83) = "No Record"
model = Sheets("MFT RPT").Cells(i, 11).Value
trimModel = Replace(Replace(model, " ", ""), "-", "")
For j = 1 To lastCollateralRow
If trimModel = Sheets("Promosheet Table").Cells(j, 1).Value Then
Sheets("MFT RPT").Cells(i, 83) = Sheets("promosheet Table").Cells(j, 3).Value
End If
Next j
NextIteration:
Next i
This ia just a proof of concept, you will need to adjust variables and ranges to suit your needs.
Sub ProofOfConcept()
Dim rngList As Range
Dim rngMatch As Range
Dim arrList As Variant
Dim arrMatch As Variant
Set rngList = Range("A1:A50000")
arrList = Application.Transpose(rngList.Value)
Set rngMatch = Range("C1:D15000")
arrMatch = Application.Transpose(rngMatch.Value)
For a = 1 To 15000
For b = 1 To 50000
If arrMatch(1, a) = arrList(b) Then
arrMatch(2, a) = "Match found"
GoTo skip
End If
Next
skip:
Next
rngMatch = WorksheetFunction.Transpose(arrMatch)
End Sub
thanks #Michal
played with it a bit. I trimmed down the run time from almost an hour to about 7 or 8 min using this code. works beautifully!!
Dim promoList As Range
Dim rngMatch As Range
Dim arrList As Variant
Dim arrMatch As Variant
Dim z
Set promoList = Sheets("promosheet table").Range("A1:A" & lastcollateralRow)
arrList = Application.Transpose(promoList.Value)
Set rngMatch = Sheets("Mft rpt").Range("K2:K" & lastRow)
arrMatch = Application.Transpose(rngMatch.Value)
For z = LBound(arrMatch) To UBound(arrMatch)
arrMatch(z) = Replace(Replace(arrMatch(z), " ", ""), "-", "")
Next
For A = 1 To lastRow
If Cells(A + 1, 83).Value <> "" Then GoTo skip:
Sheets("mft rpt").Cells(A + 1, 83) = "No Record"
For b = 1 To lastcollateralRow + 1
If arrMatch(A) = promoList(b) Then
Sheets("mft rpt").Cells(A + 1, 83) = promoList(b, 3)
GoTo skip
End If
Next
skip:
Next
I am using the below VBA code to compare 60K records (only a particular string) from 'Sheet1' with 7K records from 'Sheet2'. It's taking a very long time to complete and is unresponsive at times. Is there anyway to improve the performance of this subroutine?
Sub txtext()
Dim fnl As String, stl As String, env As String, typ As String
Dim ctm As String
Dim stdate As Date, enddate As Date
lr = Sheets("cid_match").Range("A" & Rows.Count).End(xlUp).Row
cr = Sheets("scme").Range("C" & Rows.Count).End(xlUp).Row
stdate = Now
m = 2
For Each e In Sheets("cid_match").Range("BI2:BI" & lr).Cells
stl = Worksheets("cid_match").Range("BI" & m).Cells.Value
typ = Worksheets("cid_match").Range("BK" & m).Cells.Value
s = 2
For Each r In Sheets("scme").Range("C2:C" & cr).Cells
ctm = Worksheets("scme").Range("B" & s).Cells.Value
fnl = r.Value
If InStr(fnl, stl) > 0 And ctm = typ Then
Worksheets("cid_match").Range("BJ" & m).Value = fnl
GoTo sss
End If
s = s + 1
Next r
sss:
m = m + 1
Next e
enddate = Now
MsgBox "Succesfully Completed!!! Started at " & stdate & " Ended at " & enddate
End Sub
In order to optimize searching for a record you want to firstly sort your data using either quicksort or bubblesort. Then you will be able to search through using binary search. This will significantly reduce your wait times. Lucky you, these functions have already been written in VBA by other developers.
Bubblesort
Sub BubbleSort(list())
' Sorts an array using bubble sort algorithm
Dim First As Integer, Last As Long
Dim i As Long, j As Long
Dim Temp As Long
First = LBound(list)
Last = UBound(list)
For i = First To Last - 1
For j = i + 1 To Last
If list(i) > list(j) Then
Temp = list(j)
list(j) = list(i)
list(i) = Temp
End If
Next j
Next i
End Sub
Binary Search
Function arrayFind(theArray() As Integer, target As Integer) As Boolean
Dim low As Integer
low = 0
Dim high As Integer
high = UBound(theArray)
Dim i As Integer
Dim result As Boolean
Do While low <= high
i = (low + high) / 2
If target = theArray(i) Then
arrayFind = True
Exit Do
ElseIf target < theArray(i) Then
high = (i - 1)
Else
low = (i + 1)
End If
Loop
If Not arrayFind Then
arrayFind = False
End If
End Function
Try the below: -
Sub Sample()
Dim AryLookup() As String
Dim DteStart As Date
Dim LngCounter As Long
Dim LngRow As Long
Dim WkSht As Worksheet
DteStart = Now
'Look at the lookup worksheet
Set WkSht = ThisWorkbook.Worksheets("scme")
'Make an array the same size as the dataset
ReDim AryLookup(WkSht.Range("C" & Rows.Count).End(xlUp).Row)
'Copy the dataset in
For LngRow = 2 To UBound(AryLookup, 1)
AryLookup(LngRow - 2) = WkSht.Range("C" & LngRow)
DoEvents
Next
Set WkSht = Nothing
'Look at the source worksheet
Set WkSht = ThisWorkbook.Worksheets("cid_match")
'Work from the bottom up so not to be falsly stopped by an empty row
'Step -1 means go backwards by one with each itteration of the loop
For LngRow = WkSht.Range("A" & Rows.Count).End(xlUp).Row To 2 Step -1
'Look through the loop for a match
For LngCounter = 0 To UBound(AryLookup, 1)
'If it matches then add it to the column and exit the loop
If InStr(1, WkSht.Range("BI" & LngRow), AryLookup(LngCounter)) > 0 Then
WkSht.Range("BJ" & LngRow).Value = AryLookup(LngCounter)
Exit For
End If
DoEvents
Next
DoEvents
Next
Set WkSht = Nothing
MsgBox "Succesfully Completed!!! " & vbNewLine & "Started at " & DteStart & vbNewLine & "Ended at " & Now
End Sub
Looking over your code was quite confusing to follow what was happening, in some cases I couldn't understand or see the benefit in what was happening in the code, so the above may not be correct. You should always write your code with the assumption that another developer will need to pick it up and support it one day, it'll help you in the future if you have to return to it years later and want to remember what is happening.
I Have a Red Lion Data station that records temperatures from about 25 ovens and the data is stored on a network in CSV files. I would like to import this data into an Excel file once every two minutes, but only import the changes after the initial import. After the import, the VBA must release the CSV file for the 2 minutes so that it can be updated by the data station. I have searched the web, this site and the closest thing I have found is the following code. This code looks for changes but it does not import the file. any help would be appreciated.
Dim NextTime As Date
Function LastModTime(FileSpec As String) As Date
'Returns the date-time the file specified by FileSpec (path string) was last modified
Dim fs, f, s
Set fs = CreateObject("Scripting.FileSystemObject")
Set f = fs.GetFile(FileSpec)
LastModTime = f.DateLastModified
End Function
Sub Check4Changes()
'Checks the file FilePath for changes every 60 seconds
'If file has changed, pops up a message box. Stores the
'last modified time in cell M1 of Sheet1
ChDir "Q:\Manufacturing\Equipment\DispatchLogs\logs\7-DES"
Const FilePath As String = "Q:\Manufacturing\Equipment\DispatchLogs\logs\7-DES\*.csv"
Dim LastMod As Date
On Error GoTo ReSchedule
LastMod = LastModTime(FilePath)
With Worksheets("Sheet1").Range("C1")
If IsEmpty(.Value) Then
.Value = LastMod
GoTo ReSchedule
ElseIf .Value < LastMod Then
.Value = LastMod
MsgBox FilePath & " updated.", vbInformation, "Check4Changes"
End If
End With
ReSchedule:
'Reschedule this same routine to run in one minute.
NextTime = Now + 2 / 1440
Application.StatusBar = "Next check at " & NextTime
Application.OnTime NextTime, "Check4Changes"
End Sub
Sub CancelChecking()
Application.OnTime NextTime, "Check4Changes", Schedule:=False
Application.StatusBar = False
End Sub
I have written some code which will give you the basic idea
Sub Main()
Dim Wbk_CSV As Excel.Workbook
Dim Excel_Wbk As Excel.Workbook
Dim Var_WholeCSVData As Variant
Dim Var_ExcelData As Variant
Dim Var_ToUpdate As Variant
Dim NumOfRows As Long
Dim Last_Row As Long
Set Wbk_CSV = Workbooks.Open("PathWithFileName")
Wbk_CSV.Sheets(1).Activate
Last_Row = ActiveSheet.Cells.SpecialCells(xlCellTypeLastCell).Row
'Taking whole data in a variant
'Change the range as per the data in csv file
Var_WholeCSVData = Wbk_CSV.Sheets(1).Range("A2:D" & Last_Row).Value 'Considering first row as header row and there are 4 columns
'After taking whole data in varinat close csv file without saving
Wbk_CSV.Close savechanges:=False
Set Wbk_CSV = Nothing
'Now open excel file in which data will be updated
Set Excel_Wbk = Workbooks.Open("PathWithFileName")
Excel_Wbk.Sheets(1).Activate
Last_Row = ActiveSheet.Cells.SpecialCells(xlCellTypeLastCell).Row
Var_ExcelData = Excel_Wbk.Sheets(1).Range("A2:D" & Last_Row).Value 'Considering first row as header row
NumOfRows = 0
'This function will return count of updated rows and data to update
Var_ToUpdate = Delete_Duplicates(Var_WholeCSVData, Var_ExcelData, NumOfRows)
Excel_Wbk.Sheets(1).Activate
'paste data
If NumOfRows > 0 Then
ActiveSheet.Range("A" & Last_Row + 1 & ":D" & Last_Row + NumOfRows).Value = Var_ToUpdate
End If
Excel_Wbk.Close savechanges:=True
Set Excel_Wbk = Nothing
'result
MsgBox ("Number of rows imported: " & NumOfRows)
End Sub
Function Delete_Duplicates(Var_FromCSV As Variant, Var_FromExcel As Variant, ByRef NumberOfRowToupdate As Long) As Variant
'using dictinpary objects
Dim dict_Duplicates As Object
Dim i_AddToDict, i, j As Long
Dim Str_value As String
Dim Var_Temp As Variant
Dim lng_temp As Long
Set dict_Duplicates = CreateObject("Scripting.Dictionary")
ReDim Var_Temp(1 To UBound(Var_FromCSV, 1), 1 To UBound(Var_FromCSV, 2))
'Add excel data to dict. by concatenating
'All unique values will be added
For i_AddToDict = 1 To UBound(Var_FromCSV)
Str_value = CStr(Var_FromExcel(i_AddToDict, 1) & Var_FromExcel(i_AddToDict, 2) & Var_FromExcel(i_AddToDict, 3) & Var_FromExcel(i_AddToDict, 4))
If dict_Duplicates.exists(Str_value) Then
'do nothing
Else
dict_Duplicates.Add Str_value, 1
End If
Next i_AddToDict
'looking for values which are not available in excel file
For i = 1 To UBound(Var_FromCSV)
Str_value = CStr(Var_FromCSV(i_AddToDict, 1) & Var_FromCSV(i_AddToDict, 2) & Var_FromCSV(i_AddToDict, 3) & Var_FromCSV(i_AddToDict, 4))
If dict_Duplicates.exists(Str_value) Then
'do nothing
Else
'storing values in a variant
For j = 1 To 4
Var_Temp(lng_temp, j) = Var_FromCSV(i, j)
Next j
lng_temp = lng_temp + 1
dict_Duplicates.Add Str_value, 1
End If
Next i
NumberOfRowToupdate = lng_temp - 1
Delete_Duplicates = Var_Temp
End Function
Sub Add_sumf()
Dim i As Integer
i = 3
Dim cellDate As Integer
cellDate = 0
Dim cellDate1 As Date
cellDate1 = TimeValue("00:00:00")
Dim total As Integer
total = 0
Dim j As Integer
j = 2
Dim k As Integer
k = 2
Set aa = Workbooks("Book3").Worksheets(1)
Set bb = Workbooks("Final_result").Worksheets(1)
Do While bb.Cells(1, k).Value <> ""
For Each y In bb.Range("A:A")
On Error GoTo Label
If UCase(bb.Cells(j, "A").Value) <> "" Then
cellDate1 = WorksheetFunction.SumIfs(aa.Range("F:F"), aa.Range("B:B"), UCase(bb.Cells(1, k).Value), aa.Range("G:G"), UCase(bb.Cells(j, "A").Value))
bb.Cells(j, k).Value = TimeValue(cellDate1)
cellDate1 = TimeValue("00:00:00")
bb.Cells(j, k).NumberFormat = "[h]:mm:ss"
On Error GoTo Label
j = j + 1
Else
Exit For
End If
Next
j = 2
k = k + 1
Loop
Label:
'MsgBox Err.Description
Exit Sub
End Sub
I am using above code to add time duration based upon value of two other columns but I always get 00:00:00 as result.
if i use below code i get the answer but its too slow very slow
Sub add_it_time()
Dim i As Integer
i = 3
Dim cellDate As Integer
cellDate = 0
Dim cellDate1 As Date
cellDate1 = TimeValue("00:00:00")
Dim total As Integer
total = 0
Dim j As Integer
j = 2
Dim k As Integer
k = 2
Set aa = Workbooks("Book3").Worksheets(1)
Set bb = Workbooks("Final_result").Worksheets(1)
Do While bb.Cells(1, k).Value <> ""
'MsgBox bb.Cells(1, k).Value
For Each y In bb.Range("A:A")
On Error GoTo Label
' MsgBox UCase(bb.Cells(j, "A").Value)
If UCase(bb.Cells(j, "A").Value) <> "" Then
For Each x In aa.Range("F:F")
On Error Resume Next
If UCase(aa.Cells(i, "B").Value) = UCase(bb.Cells(j, "A").Value) Then
' MsgBox aa.Cells(i, "F").Text
' total = total + Int(get_Second(aa.Cells(i, "F").Text))
If UCase(aa.Cells(i, "G").Value) = UCase(bb.Cells(1, k).Value) Then
'MsgBox aa.Cells(i, "F").Text
cellDate1 = cellDate1 + TimeValue(aa.Cells(i, "F").Value)
End If
End If
i = i + 1
Next
i = 3
On Error GoTo Label
bb.Cells(j, k).NumberFormat = "h:mm:ss"
bb.Cells(j, k).Value = WorksheetFunction.Text(cellDate1, "[hh]:mm:ss")
total = 0
cellDate1 = 0
j = j + 1
Else
Exit For
End If
Next
j = 2
k = k + 1
Loop
Label:
'MsgBox Err.Description
Exit Sub
End Sub
The source column which contains date is of general formatt
I am new to VBA macros
UPDATED SOLUTION:
After discussion in chat with OP it was decided that pure formula solution is fine - below are formulas / actions to do on the separate sheet starting A1:
Row A will be resulting table header: in A1 I added Agent Name / Release Code, and starting B1 there's a list of all available Release Code values (easily got using Remove Duplicates).
I defined the following named ranges for the simplicity and effectiveness (since initial data is NOT static): AgentNames=OFFSET('Agent State'!$B$2,0,0,COUNTA('Agent State'!$B:$B)-1,1) - this will return the range of names on the initial sheet excluding the header; TimeInStateData=OFFSET(AgentNames,0,4) and ReleaseCodes=OFFSET(AgentNames,0,5) as shifted AgentNames range.
In column A we should obtain the list of names, which should be unique, so select in column A any number of cells which is NOT less that number of unique names - for the sample I used A2:A51, and type that formula: =IFERROR(INDEX(AgentNames,SMALL(IF(MATCH(AgentNames,AgentNames,0)=ROW(INDIRECT("1:"&ROWS(AgentNames))),MATCH(AgentNames,AgentNames,0),""),ROW(INDIRECT("1:"&ROWS(AgentNames))))),"") and press CTRL+SHIFT+ENTER instead of usual ENTER - this will define a Multicell ARRAY formula and will result in curly {} brackets around it (but do NOT type them manually!).
B2: =IF(OR($A2="",SUMPRODUCT(--($A2=AgentNames),--(B$1=ReleaseCodes),TIMEVALUE(TimeInStateData))=0),"",SUMPRODUCT(--($A2=AgentNames),--(B$1=ReleaseCodes),TIMEVALUE(TimeInStateData))) - normal formula, which will return empty value for either empty name or zero time.
Copy formula from B2 to the whole table.
Remarks:
Resulting range for the sum of time values should be formatted as Time.
If the list of names should be expanded in the future - repeat step 3 for the new range, but do NOT drag the formula down - this will result in You cannot change part of an array error.
Sample file: https://www.dropbox.com/s/quudyx1v2fup6sh/AgentsTimeSUM.xls
INITIAL ANSWER:
Perhaps that's too simple and obvious, but at a glance I don't understand why you have that line of code:
cellDate1 = TimeValue("00:00:00")
right after your SUMIFS: cellDate1 = WorksheetFunction.SumIfs(aa.Range("F:F"), ...
Try to remove the first one where you assign zeros to cellDate1.
This might be a little tricky, even with VBA...
I have comma separated lists in cells based on start times over 5 minutes intervals but I need to remove times that are only 5 apart.
The numbers are text, not time at this point. For example, one list would be 2210, 2215, 2225, 2230, 2240 (the start times).
In this case, 2215 and 2230 should be removed but I also need to remove the opposite numbers (i.e.,2210 and 2225) in other cases (the end times).
Someone helped me with my specs:
A cell contains times: t(1), t(2), t(3), ... t(n). Starting at time t(1), each value in the list is examined. If t(x) is less than 6 minutes after t(x-1) delete t(x) and renumber t(x+1) to t(n).
Input:
2210, 2215, 2225, 2230, 2240
Output:
column1: 2210
column2: 2240
This does what I think you require.
Option Explicit
Sub DeleteSelectedTimes()
Dim RowCrnt As Long
RowCrnt = 2
Do While Cells(RowCrnt, 1).Value <> ""
Cells(RowCrnt, 1).Value = ProcessSingleCell(Cells(RowCrnt, 1).Value, 1)
Cells(RowCrnt, 2).Value = ProcessSingleCell(Cells(RowCrnt, 2).Value, -1)
RowCrnt = RowCrnt + 1
Loop
End Sub
Function ProcessSingleCell(ByVal CellValue As String, ByVal StepFactor As Long) As String
Dim CellList() As String
Dim CellListCrntStg As String
Dim CellListCrntNum As Long
Dim InxCrnt As Long
Dim InxEnd As Long
Dim InxStart As Long
Dim TimeCrnt As Long ' Time in minutes
Dim TimeLast As Long ' Time in minutes
CellList = Split(CellValue, ",")
If StepFactor = 1 Then
InxStart = LBound(CellList)
InxEnd = UBound(CellList)
Else
InxStart = UBound(CellList)
InxEnd = LBound(CellList)
End If
CellListCrntStg = Trim(CellList(InxStart))
If (Not IsNumeric(CellListCrntStg)) Or InStr(CellListCrntStg, ".") <> 0 Then
' Either this sub-value is not numeric or if contains a decimal point
' Either way it cannot be a time.
ProcessSingleCell = CellValue
Exit Function
End If
CellListCrntNum = Val(CellListCrntStg)
If CellListCrntNum < 0 Or CellListCrntNum > 2359 Then
' This value is not a time formatted as hhmm
ProcessSingleCell = CellValue
Exit Function
End If
TimeLast = 60 * (CellListCrntNum \ 100) + (CellListCrntNum Mod 100)
For InxCrnt = InxStart + StepFactor To InxEnd Step StepFactor
CellListCrntStg = Trim(CellList(InxCrnt))
If (Not IsNumeric(CellListCrntStg)) Or InStr(CellListCrntStg, ".") <> 0 Then
' Either this sub-value is not numeric or if contains a decimal point
' Either way it cannot be a time.
ProcessSingleCell = CellValue
Exit Function
End If
CellListCrntNum = Val(CellListCrntStg)
If CellListCrntNum < 0 Or CellListCrntNum > 2359 Then
' This value is not a time formatted as hhmm
ProcessSingleCell = CellValue
Exit Function
End If
TimeCrnt = 60 * (CellListCrntNum \ 100) + (CellListCrntNum Mod 100)
If Abs(TimeCrnt - TimeLast) < 6 Then
' Delete unwanted time from list
CellList(InxCrnt) = ""
Else
' Current time becomes Last time for next loop
TimeLast = TimeCrnt
End If
Next
CellValue = Join(CellList, ",")
If Left(CellValue, 1) = "," Then
CellValue = Mid(CellValue, 2)
CellValue = Trim(CellValue)
End If
Do While InStr(CellValue, ",,") <> 0
CellValue = Replace(CellValue, ",,", ",")
Loop
ProcessSingleCell = CellValue
End Function
Explanation
Sorry for the lack of instructions in the first version. I assumed this question was more about the technique for manipulating the data than about VBA.
DeleteSelectedTimes operates on the active worksheet. It would be easy to change to work on a specific worksheet or a range of worksheets if that is what you require.
DeleteSelectedTimes ignores the first row which I assume contains column headings. Certainly my test worksheet has headings in row 1. It then processes columns A and B of every row until it reaches a row with an empty column A.
ProcessSingleCell has two parameters: a string and a direction. DeleteSelectedTimes uses the direction so values in column A are processed left to right while values in column B are processed right to left.
I assume the #Value error is because ProcessSingleCell does not check that the string is of the format "number,number,number". I have changed ProcessSingleCell so if the string is not of this format, it does change the string.
I have no clear idea of what you do or do not know so come back with more questions as necessary.
Still not clear on your exact requirements, but this might help get you started....
Sub Tester()
Dim arr
Dim out As String, x As Integer, c As Range
Dim n1 As Long, n2 As Long
For Each c In ActiveSheet.Range("A1:A10")
If InStr(c.Value, ",") > 0 Then
arr = Split(c.Value, ",")
x = LBound(arr)
out = ""
Do
n1 = CLng(Trim(arr(x)))
n2 = CLng(Trim(arr(x + 1)))
'here's where your requirements get unclear...
out = out & IIf(Len(out) > 0, ", ", "")
If n2 - n1 <= 5 Then
out = out & n1 'skip second number
x = x + 2
Else
out = out & n1 & ", " & n2 'both
x = x + 1
End If
Loop While x <= UBound(arr) - 1
'pick up any last number
If x = UBound(arr) Then
out = out & IIf(Len(out) > 0, ", ", "") & arr(x)
End If
c.Offset(0, 1).Value = out
End If
Next c
End Sub
Obviously many ways to skin this cat ... I like to use collections for this sort of thing:
Private Sub PareDownList()
Dim sList As String: sList = ActiveCell ' take list from active cell
Dim vList As Variant: vList = Split(sList, ",") ' convert to variant array
' load from var array into collection
Dim cList As New Collection
Dim i As Long
For i = 0 To UBound(vList): cList.Add (Trim(vList(i))): Next
' loop over collection removing unwanted entries
' (in reverse order, since we're removing items)
For i = cList.Count To 2 Step -1
If cList(i) - cList(i - 1) = 5 Then cList.Remove (i)
Next i
' loop to put remaining items back into a string fld
sList = cList(1)
For i = 2 To cList.Count
sList = sList + "," + cList(i)
Next i
' write the new string to the cell under the activecell
ActiveCell.Offset(1) = "'" + sList ' lead quote to ensure output cell = str type
End Sub
' If activecell contains: "2210, 2215, 2225, 2230, 2240"
' the cell below will get: "2210,2225,2240"
Note: this sample code should be enhanced w some extra validation & checking (e.g. as written assumes all good int values sep by commas & relies in implicit str to int conversions). Also as written will convert "2210, 2215, 2220, 2225, 2230, 2240" into "2210, 2040" - you'll need to tweak the loop, loop ctr when removing an item if that's not what you want.