I have a sheet i am working on that i need to populate all the days between 2 dates for a month 54 times.
I have got together a loop that can do this for the first section - I now need ti replicated 54 times.
I have figured out a loop to copy and paste this range the 54 times which works as it should. however I am wondering whether there is a way to put the date generation loop inside the duplication loop and generate every date rather than copy and paste?
I am mainly looking for the most efficient method as this will potentially be scaled up in future so any pointers with my code would be greatly appreciated.
Sub WriteDatesLoopTest()
'Disables Screen Flickering on Copy/Paste
Application.ScreenUpdating = False
OffsetValue = 42
'----------------------------------------------
Dim StartDate As Range
Dim EndDate As Range
Dim OutputRange As Range
Dim ClearRange As Range
Dim StartValue As Variant
Dim EndValue As Variant
Dim DateRangeCopy As Range
Dim EmployeeCount As Range
Dim MonthValue As Range
'----------------------------------------------
Set ClearRange = Range("A9:A39")
Set StartDate = Range("T4")
Set EndDate = Range("T5")
Set OutputRange = Range("A9")
Set DateRangeCopy = Range("A9:A39")
Set EmployeeCount = Range("O1")
Set MonthValue = Range("J1")
StartValue = StartDate
EndValue = EndDate
'----------Date Generation Loop----------------
If EndValue - StartValue <= 0 Then
Exit Sub
End If
ColIndex = 0
For i = StartValue To EndValue
OutputRange.Offset(ColIndex, 0) = i
ColIndex = ColIndex + 1
Next
'----------Copy & Paste------------------------
n = EmployeeCount
For j = 0 To (n - 1)
'ClearRange.Offset(OffsetValue * j, 0).ClearContents
DateRangeCopy.Copy
With DateRangeCopy.Offset(OffsetValue * j, 0)
.PasteSpecial Paste:=xlPasteValuesAndNumberFormats
SkipBlanks = False
End With
'Show Status Bar in Bottom Left
Application.StatusBar = "Progress: " & Format(j / n, "0%")
Next
'Display Message on completion
MsgBox "Dates Generated"
'Removes 'Walking Ants' From copied selection
Application.CutCopyMode = False
'Enables Screen Flickering on Copy/Paste
Application.ScreenUpdating = True
'Reset Status Bar in Bottom Left
Application.StatusBar = False
'-----------------------------------
End Sub
Thank you
Just seen the comments. Yes Code Review would be good. You probably want to move the entire process into an array.
This demonstrates all the required elements.
Option Explicit
Public Sub GenerateDates()
Const LOOPCOUNT As Long = 54
Dim i As Long, j As Long
Dim startDate As Long, endDate As Long, rowCounter As Long
startDate = CLng(Now)
endDate = startDate + 7
Application.ScreenUpdating = False
With ActiveSheet
For i = 1 To LOOPCOUNT
For j = startDate To endDate
rowCounter = rowCounter + 1
.Cells(rowCounter, 1) = j
Next j
rowCounter = rowCounter + 5 '<== Add gap
Next i
.Columns("A").NumberFormat = "m/d/yyyy"
End With
Application.ScreenUpdating = True
End Sub
Doing the same thing in memory (I have included a second dimension as you may have additional columns in your data. My principle was really about showing the dates increment with row gap.)
Option Explicit
Public Sub GenerateDates() '697
Const LOOPCOUNT As Long = 54
Dim i As Long, j As Long
Dim startDate As Long, endDate As Long, rowCounter As Long
startDate = CLng(Now)
endDate = startDate + 7
Dim ROWGAP As Long: ROWGAP = 41-(Enddate-StartDate)
Dim outputArr()
ReDim outputArr(1 To (((endDate - startDate + 1) + ROWGAP) * LOOPCOUNT) - ROWGAP, 1 To 1)
Application.ScreenUpdating = False
With ActiveSheet
For i = 1 To LOOPCOUNT
For j = startDate To endDate
rowCounter = rowCounter + 1
outputArr(rowCounter, 1) = j
Next j
rowCounter = rowCounter + ROWGAP '<== Add gap
Next i
.Cells(1, 1).Resize(UBound(outputArr), UBound(outputArr, 2)) = outputArr 'This is only with one dimensional
.Columns("A").NumberFormat = "m/d/yyyy"
End With
Application.ScreenUpdating = True
End Sub
tl;dr;
The principle is basically that you want an outer loop that increments from 1 to 54. Then an inner loop that increments from start date to end date. I treat date as a Long and simply add one to the startDate until I reach the endDate in the inner loop. For i = 1 To LOOPCOUNT is doing the repeat work... here you could be using your copy paste. I increment the rowCounter variable by 5 before the next repeat to leave some blank rows between repeats.
The first version writes to the sheet for every row with .Cells(rowCounter, 1) = j . That is an expensive operation "touching" the sheet each time. The second version does the same process but doesn't write to the sheet until the very end. Instead, it writes to an array. This is much faster as is all done in memory (no going to disk).
I know how many rows I will have in the array because I know how many times I am repeating the entire process (54), the number of days from startDate and endDate (8) and the number of padding rows I am adding (5). So I can size my array to write to with ReDim outputArr(1 To (((endDate - startDate + 1) + ROWGAP) * LOOPCOUNT) - ROWGAP, 1 To 1). I don't need 5 rows padding on the 54th loop so I remove these from the total row count.
For understanding working with arrays and data in the worksheet the article VBA Arrays And Worksheet Ranges is worth a read, a long with the more general VBA Arrays
The fewer tasks that a subroutine performs, the easier it is to write, test, and modify. For this reason I created a function to generate the output Array.
OffsetValue has a somewhat ambiguous name. I used SectionLength instead.
Sub AddDates()
Const OffsetValue = 42
Dim data() As Variant
data = getDatesArray(#6/1/2018#, #6/30/2018#)
With Worksheets("Sheet1")
.Columns(1).ClearContents
.Range("A1").Resize(UBound(data)).Value = data
End With
End Sub
Function getDatesArray(StartDate As Date, EndDate As Date, Optional SectionLength As Long = 42, Optional RepeatCount As Long = 54) As Variant()
Dim results() As Variant
Dim count As Long, n As Long
ReDim results(1 To SectionLength * RepeatCount, 1 To 1)
If EndDate >= StartDate Then
Do
count = count + 1
For n = 0 To UBound(results) - SectionLength Step SectionLength
results(n + count, 1) = StartDate
Next
StartDate = StartDate + 1
Loop Until StartDate = EndDate
End If
getDatesArray = results
End Function
Related
I want to randomly select 50 rows from one sheet and pasting them in a separate workbook for data sampling. I don't know how to do it because first, I'm new to VBA, I want to learn something new and second, I tried searching this on Google but no accurate answer found.
So what's on my mind is this:
I'll get first the number of rows in that worksheet. I've already
done it with this one line of code:
CountRows = ActiveSheet.Cells(ActiveSheet.Rows.Count, "A").End(xlUp).Row
Get a random number from 1 to CountRows uniquely. The random numbers should be incremental (1,5,7,20,28,30,50 and no backward counting). Then grab that row, create a new workbook if not yet open and paste it there.
How can I achieve this process? I have no idea how to start this.
First, generate an array of 50 unique numbers between 1 and CountRows, using this routine:
' Generate a sorted array(0 to count-1) numbers between a and b inclusive
Function UniqueRandom(ByVal count As Long, ByVal a As Long, ByVal b As Long) As Long()
Dim i As Long, j As Long, x As Long
ReDim arr(b - a) As Long
Randomize
For i = 0 To b - a: arr(i) = a + i: Next
If b - a < count Then UniqueRandom = arr: Exit Function
For i = 0 To b - a 'Now we shuffle the array
j = Int(Rnd * (b - a))
x = arr(i): arr(i) = arr(j): arr(j) = x ' swap
Next
' After shuffling the array, we can simply take the first portion
ReDim Preserve arr(0 To count - 1)
'sorting, probably not necessary
For i = 0 To count - 1
For j = i To count - 1
If arr(j) < arr(i) Then x = arr(i): arr(i) = arr(j): arr(j) = x ' swap
Next
Next
UniqueRandom = arr
End Function
Now you can use the above routine to generate random, unique and sorted indexes and copy the corresponding rows. Here's an example:
Sub RandomSamples()
Const sampleCount As Long = 50
Dim lastRow As Long, i As Long, ar() As Long, rngToCopy As Range
With Sheet1
lastRow = .Cells(.Rows.count, "A").End(xlUp).row
ar = UniqueRandom(sampleCount, 1, lastRow)
Set rngToCopy = .Rows(ar(0))
For i = 1 To UBound(ar)
Set rngToCopy = Union(rngToCopy, .Rows(ar(i)))
Next
End With
With Workbooks.Add
rngToCopy.Copy .Sheets(1).Cells(1, 1)
.SaveAs ThisWorkbook.path & "\" & "samples.xlsx"
.Close False
End With
End Sub
Following code will do what you need.
Sub Demo()
Dim lng As Long
Dim tempArr() As String
Dim srcWB As Workbook, destWB As Workbook
Dim rng As Range
Dim dict As New Scripting.Dictionary
Const rowMax As Long = 100 'maximum number of rows in source sheet
Const rowMin As Long = 1 'starting row number to copy
Const rowCopy As Long = 50 'number of rows to copy
Dim intArr(1 To rowCopy) As Integer, rowArr(1 To rowCopy) As Integer
Set srcWB = ThisWorkbook
'get unique random numbers in dictionary
With dict
Do While .Count < rowCopy
lng = Rnd * (rowMax - rowMin) + rowMin
.Item(lng) = Empty
Loop
tempArr = Split(Join(.Keys, ","), ",")
End With
'convert random numbers to integers
For i = 1 To rowCopy
intArr(i) = CInt(tempArr(i - 1))
Next i
'sort random numbers
For i = 1 To rowCopy
rowArr(i) = Application.WorksheetFunction.Small(intArr, i)
If rng Is Nothing Then
Set rng = srcWB.Sheets("Sheet1").Rows(rowArr(i))
Else
Set rng = Union(rng, srcWB.Sheets("Sheet1").Rows(rowArr(i)))
End If
Next i
'copy random rows, change sheet name and destination path as required
Set destWB = Workbooks.Add
With destWB
rng.Copy destWB.Sheets("Sheet1").Range("A1")
.SaveAs Filename:="D:\Book2.xls", FileFormat:=56
End With
End Sub
Above code uses Dictionary so you have to add reference to Microsoft Scripting Runtime Type Library. In Visual Basic Editor, go to Tools->References and check "Microsoft Scripting Runtime" in the list.
Let me know if anything is not clear.
I have a table which goes from A1 to ALL1 (1000 values), I have to find the max value of consecutive numbers for example if I had these six values:
4 37 -12 2 3 -1, the max would be 41 taken from the first two numbers. If it was
-6 -14 6 15 22 -9, it would be 43 (from 6, 15, 22).
I have to do this in VBA from randomly generated numbers (figured that part out so it's good), but can't figure out this part and then I have to return the position of the first and last value in my sequence. So please some help would be greatly appreciated as I'm not quite a VBA wizard.
Thank you :)
Consider the below example:
Sub Test()
Dim arrValues() As Variant
Dim dblTop As Double
Dim lngFirst As Long
Dim lngLast As Long
Dim i As Long
Dim j As Long
Dim dblSum As Double
Dim arrResult() As Variant
arrValues = Range("A1:ALL1")
dblTop = arrValues(1, 1)
lngFirst = 1
lngLast = 1
For i = 1 To 1000
dblSum = 0
For j = i To 1000
dblSum = dblSum + arrValues(1, j)
If dblSum > dblTop Then
lngFirst = i
lngLast = j
dblTop = dblSum
End If
Next
Next
Debug.Print "Max value: " & dblTop
Debug.Print "First index: " & lngFirst
Debug.Print "Last index: " & lngLast
arrResult() = Array()
For k = lngFirst To lngLast
ReDim Preserve arrResult(UBound(arrResult) + 1)
arrResult(UBound(arrResult)) = arrValues(1, k)
Next
Debug.Print "Sequence: " & Join(arrResult, " + ")
End Sub
For the set of the values e. g. as in the question
It returns the following output
Just to be a little different, and because I did the work before my weekend blew up, here is a function that will return a range:
Function MAXSUM(r As Range) As Range
Dim i&, j&
Dim rng As Range
Dim sm As Double
Dim ws As Worksheet
Set ws = ActiveSheet
sm = r.Item(1) + r.Item(2)
For i = 1 To r.Cells.Count - 1
For j = i + 1 To r.Cells.Count
With ws
If WorksheetFunction.Sum(.Range(r(i), r(j))) > sm Then
Set rng = .Range(r(i), r(j))
sm = WorksheetFunction.Sum(.Range(r(i), r(j)))
End If
End With
Next j
Next i
Set MAXSUM = rng
The beauty of this is:
1.It returns the range of the max sum.
2.It is not dependent on being in the first row, it can be a column, a row, or multiples of each. It will look left to right first then top to bottom of the range.
3.It can be called from vba and/or directly as a UDF in the worksheet(see below)
To call from vba:
Sub getmax()
Dim rng As Range
Dim ws As Worksheet
Set ws = ActiveSheet
Set rng = MAXSUM(ws.Range("A1 :AAL1"))
Debug.Print rng.Address 'gets the address of range
Debug.Print WorksheetFunction.Sum(rng) 'gets the sum of the range
End Sub
This just shows a few things, but because it returns a range it is possible to do anything one can do with a given range.
To call from the worksheet directly:
To get the sum:
=SUM(MAXSUM(*YourRange*))
To get the range:
First Cell:
=ADDRESS(ROW(MAXSUM(*YourRange*)),COLUMN(MAXSUM(*YourRange*)))
Last cell:
=ADDRESS(ROW(MAXSUM(*YourRange*))+ROWS(MAXSUM(*YourRange*))-1,COLUMN(MAXSUM(*YourRange*))+COLUMNS(MAXSUM(*YourRange*))-1)
Full Address:
=ADDRESS(ROW(MAXSUM(*YourRange*)),COLUMN(MAXSUM(*YourRange*)))&":"&ADDRESS(ROW(MAXSUM(*YourRange*))+ROWS(MAXSUM(*YourRange*))-1,COLUMN(MAXSUM(*YourRange*))+COLUMNS(MAXSUM(*YourRange*))-1)
Basically the formula MAXSUM(*YourRange*) works like a named range, and anything you can do with a named range you can do with this.
One note: This currently assumes that the user wants the sum of at least two numbers and therefore if the entire range is negative, or only one consecutive positive it will return the sum of the two consecutive cells that give the highest sum. To make it so it will return the highest one cell in the case of all negative or only one consecutive positive cells then remove the +1 and -1 from the beginning of the for loops.
This will do it:
Sub msa()
Dim j&, cur&, max&, ndx&, ndx1&, ndx2&, a: ndx = 1
a = [A1:ALL1]
For j = 1 To UBound(a, 2)
cur = cur + a(1, j)
Select Case True
Case cur > max: max = cur: ndx2 = j: ndx1 = ndx
Case cur <= 0: cur = 0: ndx = j + 1
End Select
Next
MsgBox max & vbLf & ndx1 & vbLf & ndx2
End Sub
My answer is based on Kadane's algorithm which has a time complexity of O(n), which is dramatically more efficient than the brute force O(n*n) time complexity of the other answer.
.
UPDATE
To handle the edge case of all negative numbers as well, you can use this version:
Sub msa()
Dim j&, k&, m&, n&, cur&, max&, ndx&, ndx1&, ndx2&, a: ndx = 1: m = -2 ^ 31
a = [A1].CurrentRegion.Resize(1)
For j = 1 To UBound(a, 2)
k = a(1, j)
If k > m Then m = k: n = j
cur = cur + k
Select Case True
Case cur > max: max = cur: ndx2 = j: ndx1 = ndx
Case cur <= 0: cur = 0: ndx = j + 1
End Select
Next
If max = 0 Then max = m: ndx1 = n: ndx2 = n
MsgBox max & vbLf & ndx1 & vbLf & ndx2
End Sub
This the code I am trying to run:
Option Explicit
Sub Test()
'-------------Declarations-------------------
Dim FinalRow, Sum As Long
Dim i, j, l, d, k, count As Integer
Dim custID(), amtPur() As Long
Dim ws As Worksheet
Set ws = Sheets("Data")
FinalRow = ws.Range("B90000").End(xlUp).Row
j = 0
'-------------Get All the Data-------------------
With ws
For i = 4 To FinalRow
custID(j) = ws.Range("B" & i).Value 'Error Here
amtPur(j) = ws.Range("C" & i).Value 'Error Here
j = j + 1
Next i
End With
'-------------Match it and present the output----
l = 4
Dim wk As Worksheet
Set wk = Sheets("Results")
With wk
For j = 0 To FinalRow
Sum = amtPur(j)
'For the first iteration
If j = 0 Then
For k = j + 1 To FinalRow
If custID(j) = custID(k) Then
Sum = amtPur(k) + Sum
Else: End If
Next k
wk.Range("A" & 3).Value = custID(j).Value
wk.Range("B" & 3).Value = Sum
Else: End If
'For the rest iterations
count = 0
d = j
Do While (d >= 0)
If custID(d) = custID(j) Then
count = count + 1
Else: End If
d = d - 1
Loop
If count <= 1 Then 'Check if instance was already found
For k = j + 1 To FinalRow
If custID(j) = custID(k) Then
Sum = amtPur(k) + Sum
Else: End If
Next k
wk.Range("A" & l).Value = custID(j).Text
wk.Range("B" & l).Value = Sum
l = l + 1
End If
Next j
End With
End Sub
but unfortunately am getting:
Subscript out of Range - Run time error 9
when I try to run it.
While you have declared your custID() and amtPur() arrays, they need to be initialised using ReDim statements before you can use them. In your case you will want to ReDim Preserve to retain values already stored in the arrays during prior loops:
Sub Test()
'-------------Declarations-------------------
Dim FinalRow, Sum As Long
Dim i As Integer
j As Integer
l As Integer
d As Integer
k As Integer
count As Integer
Dim custID() As Long, amtPur() As Long
Dim ws As Worksheet
Set ws = Sheets("Data")
FinalRow = ws.Range("B90000").End(xlUp).Row
j = 0
'-------------Get All the Data-------------------
With ws
For i = 4 To 100
ReDim Preserve custID(0 To j)
ReDim Preserve amtPur(0 To j)
custID(j) = ws.Range("B" & i).Value 'Error Here
amtPur(j) = ws.Range("C" & i).Value 'Error Here
j = j + 1
Next i
End With
End Sub
Hmm, seems a little harsh that this question has been downvoted. You're clearly new to VBA and it does seem that you've given this a fair go. I admire people who learn through trial and error - it's certainly more than many first posters do - so I'd like to give you a pretty full answer with a bit of the theory behind it:
Dim - as mentioned, declare each type. Avoid names that are similar to existing functions, like sum.
If you declare your 'read' variable as a variant, you can read the data from the worksheet with just one line and the array will be dimensioned for you. You can also acquire custID and amtPur in the same array. I've given you an example of this in the code below in a variable called custData. Be aware that these arrays have a base of 1 rather than 0.
Your With blocks are redundant. These are meant to save you repeating the object each time you access its properties. In your code you repeat the object. I'm not a huge fan of With blocks but I've put a sample in your code so you can see how it works.
Your If ... Else ... End If blocks are a bit muddled. The logic should be If (case is true) Then do some code Else case is false, so do some other code End If. Again, I've tried to re-write your code to give you examples of this.
You are confusing looping through a Range and looping through an Array. In your code you have set the limits of the Range as 4 - FinalRow. However, this does not mean your arrays have been set to the same dimensions. Most likely, your arrays start from 0 and go to FinalRow - 4. You need to be clear about these dimensions before looping.
As Mark Fitzgerald mentions, you need to dimension your array before using it. If it's an initial dimension then you could just use Redim. If you want to increase the array's dimension whilst retaining existing values then use Redim Preserve. I've tried to give you an example of both in the code below.
Okay, so onto your code...
With the looping, array size and If mistakes, it's rather difficult to see what you're trying to do. I think you might be trying to read all the customer IDs, writing them into a unique list and then summing all the values that match each ID. The code below does that. It's not the quickest or best way, but I've tried to write the code so that you can see how each of the errors above should work. I guess it doesn't matter if I'm up the wrong path as the main aim is to give you an idea of how to manage arrays, loops and Ifs. I hope your custID and amtPur are genuinely Longs - if, for example, amtPur stands for 'amount purchased' and is, in fact, a decimal number then this code will throw and error, so make sure your values and declarations are of the same type. Your commenting etiquette is a little esoteric but I've still followed it.
Good luck with your project and keep at it. I hope this helps you:
'-------------Declarations-------------------
Dim dataSht As Worksheet
Dim resultsSht As Worksheet
Dim custData As Variant
Dim uniqueIDs() As Long
Dim summaryData() As Long
Dim counter As Integer
Dim isUnique As Boolean
Dim rng As Range
Dim i As Integer
Dim j As Integer
'-------------Get All the Data-------------------
Set dataSht = ThisWorkbook.Sheets("Data")
Set resultsSht = ThisWorkbook.Sheets("Results")
With dataSht
Set rng = .Range(.Cells(4, "B"), .Cells(.Rows.Count, "B").End(xlUp)).Resize(, 2)
End With
custData = rng.Value2 'writes worksheet to variant array
'-------------Loop through the data to find number of unique IDs----
For i = 1 To UBound(custData, 1)
isUnique = True
If i = 1 Then
'First iteration so set the counter
counter = 0
Else
'Subsequent iterations so check for duplicate ID
For j = 1 To counter
If uniqueIDs(j) = custData(i, 1) Then
isUnique = False
Exit For
End If
Next
End If
'Add the unique ID to our list
If isUnique Then
counter = counter + 1
ReDim Preserve uniqueIDs(1 To counter)
uniqueIDs(counter) = custData(i, 1)
End If
Next
'-------------Aggregate the amtPur values----
ReDim summaryData(1 To counter, 1 To 2)
For i = 1 To counter
summaryData(i, 1) = uniqueIDs(i)
'Loop through the data to sum the values for the customer ID
For j = 1 To UBound(custData, 1)
If custData(j, 1) = uniqueIDs(i) Then
summaryData(i, 2) = summaryData(i, 2) + custData(j, 2)
End If
Next
Next
'-----------Outpute the results to the worksheet----
Set rng = resultsSht.Cells(4, 1).Resize(counter, 2)
rng.Value = summaryData
I have an Excel Worksheet consisting of two columns, one of which is filled with strings and the other is emtpy. I would like to use VBA to assign the value of the cells in the empty column based on the value of the adjacent string in the other column.
I have the following code:
Dim regexAdmin As Object
Set regexAdmin = CreateObject("VBScript.RegExp")
regexAdmin.IgnoreCase = True
regexAdmin.Pattern = "Admin"
Dim i As Integer
For i = 1 To 10 'let's say there is 10 rows
Dim j As Integer
For j = 1 To 2
If regexAdmin.test(Cells(i, j).Value) Then
Cells(i, j + 1).Value = "Exploitation"
End If
Next j
Next i
The problem is that when using this loop for a big amount of data, it takes way too long to work and, most of the time, it simply crashes Excel.
Anyone knows a better way to this?
You have an unnecessary loop, where you test the just completed column (j) too. Dropping that should improve the speed by 10-50%
Dim regexAdmin As Object
Set regexAdmin = CreateObject("VBScript.RegExp")
regexAdmin.IgnoreCase = True
regexAdmin.Pattern = "Admin"
Dim i As Integer
For i = 1 To 10 'let's say there is 10 rows
If regexAdmin.test(Cells(i, 1).Value) Then
Cells(i, 1).offset(0,1).Value = "Exploitation"
End If
Next i
If the regex pattern really is simply "Admin", then you could also just use a worksheet formula for this, instead of writing a macro. The formula, which you'd place next to the text column (assuming your string/num col is A) would be:
=IF(NOT(ISERR(FIND("Admin",A1))),"Exploitation","")
In general, if it can be done with a formula, then you'd be better off doing it so. it's easier to maintain.
Try this:
Public Sub ProcessUsers()
Dim regexAdmin As Object
Set regexAdmin = CreateObject("VBScript.RegExp")
regexAdmin.IgnoreCase = True
regexAdmin.Pattern = "Admin"
Dim r As Range, N As Integer, i As Integer
Set r = Range("A1") '1st row is headers
N = CountRows(r) - 1 'Count data rows
Dim inputs() As Variant, outputs() As Variant
inputs = r.Offset(1, 0).Resize(N, 1) ' Get all rows and 1 columns
ReDim outputs(1 To N, 1 To 1)
For i = 1 To N
If regexAdmin.test(inputs(i, 1)) Then
outputs(i, 1) = "Exploitation"
End If
Next i
'Output values
r.Offset(1, 1).Resize(N, 1).Value = outputs
End Sub
Public Function CountRows(ByRef r As Range) As Long
If IsEmpty(r) Then
CountRows = 0
ElseIf IsEmpty(r.Offset(1, 0)) Then
CountRows = 1
Else
CountRows = r.Worksheet.Range(r, r.End(xlDown)).Rows.Count
End If
End Function
I was wondering if anyone had any experience imposing time limits on sections of code. I have programmed a search engine into an excel spreadsheet in VBA and there is a section of the code that removes duplicate results. Now this part can sometimes stretch on for quite a long time if given the most vague search criteria. So I would like to impose a time limit for this operation. I have looked everywhere for a solution and tried using OnTime, but it doesnt seem to work in the way I need. Ideally, I'd like an imposed time limit and then when that is reached a GoTo statement, to move it further on in the code. From what I have read the OnTime will not interrupt an operation, but will wait for it to finish instead, this is not what I want.
Thanks for your help guys.
Amy
I've added my code:
Sub RemoveDuplicates()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
'Code called upon through the other macros which will remove duplicates from all the types of search.
Application.StatusBar = "Removing Duplicates...."
Dim k As Integer
Dim SuperArray As String
Dim CheckingArray As String
Dim Duplicate As Boolean
Dim i As Integer
Dim j As Integer
Dim m As Integer
Dim Endrow As Integer
Dim Endcolumn As Integer
Dim w As Integer
Dim x As Integer
Dim n As Integer
w = 1
x = 9
Endcolumn = Module6.Endcolumn(x)
Endrow = Module6.Endrow(w)
If Worksheets("Search Engine").Cells(9, Endrow) = "Percentage Similarity" Then
Endrow = Endrow - 1
End If
For i = 9 To Endcolumn
j = 1
k = i + 1
Do While j <> Endrow + 1
SuperArray = Cells(i, j) & Superstring
Superstring = SuperArray
j = j + 1
Loop
For k = k To Endcolumn
m = 1
Do While m <> Endrow
CheckingArray = Cells(k, m) & Uberstring
Uberstring = CheckingArray
m = m + 1
Loop
If Uberstring = Superstring Then
n = 1
Do While n <> Endrow + 1
If Worksheets("Search Engine").Cells(k, n).Interior.ColorIndex = 37 Then
Worksheets("Search Engine").Cells(i, n).Interior.ColorIndex = 37
End If
n = n + 1
Loop
Rows(k).Clear
End If
Uberstring = -1
Next k
Superstring = -1
Next i
Do While i > 9
If Cells(i, 1) = Empty Then
Rows(i).Delete
End If
i = i - 1
Loop
End Sub
I assume your code must have some kind of loop, e.g. For Each, While ... Wend, Do ... Loop Until, etc.
In theses cases, extend the condition by a comparison to the Timer. This returns you a Double between 0 and 86400, indicating how many seconds have passed since midnight. Thus, you also need to account for the day break. Here is some example code showing you implementations for three different loop constructs:
Sub ExampleLoops()
Dim dblStart As Double
Dim tmp As Long
Const cDblMaxTimeInSeconds As Double = 2.5
dblStart = Timer
'Example with For loop
For tmp = 1 To 1000
tmp = 1 'to fake a very long loop, replace with your code
DoEvents 'your code here
If TimerDiff(dblStart, Timer) > cDblMaxTimeInSeconds Then GoTo Finalize 'Alternative: Exit For
Next
'Alternative example for Do loop
Do
DoEvents 'your code here
Loop Until TimerDiff(dblStart, Timer) > cDblMaxTimeInSeconds And False 'your condition here
'Alternative example for While loop
While TimerDiff(dblStart, Timer) <= cDblMaxTimeInSeconds And True 'your condtion here
DoEvents 'your code here
Wend
Finalize:
'FinalizeCode here
Exit Sub
End Sub
Function TimerDiff(dblTimerStart As Double, dblTimerEnd As Double)
Dim dblTemp As Double
dblTemp = dblTimerEnd - dblTimerStart
If dblTemp < -43200 Then 'half a day
dblTemp = dblTemp + 86400
End If
TimerDiff = dblTemp
End Function