i keep running in this issue where it will jump out of the for statements that generate the table, I've been pouring over the code for a better part of three hours and i cannot find what is going wrong so i think i need another pair of eyes.
Shared Function DrawGrid() As TableLayoutPanel
Dim dayNames As New ArrayList
dayNames.Add("Monday")
dayNames.Add("Tuesday")
dayNames.Add("Wednesday")
dayNames.Add("Thursday")
dayNames.Add("Friday")
dayNames.Add("Saturday")
dayNames.Add("Sunday")
Dim hour As Integer = 8
Dim minute As Integer = 0
Dim timeType As String = "AM"
Dim dayLength As Integer = 12
Dim timetable As New TableLayoutPanel
timetable.CellBorderStyle = TableLayoutPanelCellBorderStyle.Inset
'Loops through days one at a time this creates the labels and adds them for reference by the user but is not needed for the timetable creation
For days As Integer = 0 To 7
timetable.ColumnCount += 1
timetable.RowCount += 1
If days > 0 Then
Dim NamePos As New TableLayoutPanelCellPosition(days, 0)
Dim lblDay As New Label
lblDay.Text = CStr(dayNames.Item(days))
timetable.SetCellPosition(lblDay, NamePos)
timetable.Controls.Add(lblDay)
End If
For time As Integer = 0 To dayLength
Dim rowPos As New TableLayoutPanelCellPosition(days, time)
Dim lblTime As New Label
Dim timeString As String
timetable.RowCount += 1
If days = 0 Then
minute += 6
If minute = 6 Then
minute = 0
hour += 1
End If
If hour = 13 Then
hour = 1
timeType = "PM"
End If
timeString = "Time is " & hour & ":" & minute & "0 " & timeType
lblTime.Text = timeString
timetable.SetCellPosition(lblTime, rowPos)
timetable.Controls.Add(lblTime)
timetable.Visible = True
End If
Next
Next
timetable.GrowStyle = TableLayoutPanelGrowStyle.AddColumns
timetable.AutoSize = True
MessageBox.Show("Working")
Return timetable
End Function
Are you able to add a breakpoint to the For loop to see at what point error is being generated? If so check what the local variable values are just before it throws the exception.
An out of range exception can be because you are trying to access an array/collection item that doesn't exist.
For instance myArray(4) has 5 items, if I try to access myArray(5) I will get an out of range exception, because the index starts at 0.
The for in VB.NET is inclusive, eg :
For i = 0 To 5
Debug.WriteLine(i)' outputs : 0,1,2,3,4,5
Next
So you might want to use some -1 when looping.
This for example iterates 8 times, while you are wanting to iterate only 7 times (unless in your country you have 8 days in a week)
For days As Integer = 0 To 7
Here is a possible correction :
For days As Integer = 0 To 6
timetable.ColumnCount += 1
timetable.RowCount += 1
If days > 0 Then
Dim NamePos As New TableLayoutPanelCellPosition(days, 0)
Dim lblDay As New Label
lblDay.Text = CStr(dayNames.Item(days))
timetable.SetCellPosition(lblDay, NamePos)
timetable.Controls.Add(lblDay)
End If
For time As Integer = 0 To dayLength - 1
Dim rowPos As New TableLayoutPanelCellPosition(days, time)
Dim lblTime As New Label
Dim timeString As String
timetable.RowCount += 1
If days = 0 Then
Minute += 6
If Minute() = 6 Then
Minute = 0
Hour += 1
End If
If Hour() = 13 Then
Hour = 1
timeType = "PM"
End If
timeString = "Time is " & Hour() & ":" & Minute() & "0 " & timeType
lblTime.Text = timeString
timetable.SetCellPosition(lblTime, rowPos)
timetable.Controls.Add(lblTime)
timetable.Visible = True
End If
Next
Next
Related
I am currently attempting optimise a set of 4 variables which can have any value between 0.01 and 0.97, the total of these 4 variables must equal 1. Eventually these 4 variables will need to be entered into the spreadsheet in order to return an output (this is a cell in the spreadsheet), ideally I would like to store this output against the 4 inputted variables.
My first step was to attempt to find all the combinations possible; I did this in a very basic form which took over an hour and returned around 150,000 rows.
Next I attempted to store the variables in a class before adding them to a collection but this was still quite slow.
My next step was to add them into a multi dimensional array but this was just as slow as the collection method.
I have already added Application.ScreenUpdating = False and found that Application.Calculation = xlManual made no difference in this case.
Does anyone have any advice on how to make this quicker?
This would need to be repeated a fair amount so ideally wouldn't take an hour to produce all the combinations. I haven't included the part about getting an output as the first step is way too slow and storing those results will use the same process as getting the combinations. I added the secondselapsed after the 3rd next as this takes about 32 seconds and is easier to test with.
My code example using arrays is here:
Sub WDLPerfA()
StartTime = Timer
Application.ScreenUpdating = False
NoRows = 0
Dim combos()
ReDim combos(NoRows, 1)
'Looping through variables
For a = 1 To 97
For b = 1 To 97
For c = 1 To 97
For d = 1 To 97
Application.ScreenUpdating = False
Total = a + b + c + d
If Total = 100 Then
If NoRows = 0 Then GoTo Line1
ElseIf NoRows > 0 Then
NoRows = NoRows + 1
ReDim combos(NoRows, 1)
Line1:
combo = a & "," & b & "," & c & "," & d
combos(NoRows, 0) = combo
Else: GoTo Line2
End If
Line2:
Next
Next
Next
SecondsElapsed = Round(Timer - StartTime, 2)
Debug.Print SecondsElapsed
Next
End Sub
As an test, I used a Collection to capture all of the combinations to add up to your target value and then stored all those combinations on a worksheet. It didn't take anywhere near an hour.
You don't need GoTo and you don't need to disable ScreenUpdating. But you should always use Option Explicit (read this explanation for why).
The combination loop test is simple:
Option Explicit
Sub FourCombos()
Const MAX_COUNT As Long = 97
Const TARGET_VALUE As Long = 100
Dim combos As Collection
Set combos = New Collection
Dim a As Long
Dim b As Long
Dim c As Long
Dim d As Long
StartCounter
For a = 1 To MAX_COUNT
For b = 1 To MAX_COUNT
For c = 1 To MAX_COUNT
For d = 1 To MAX_COUNT
If (a + b + c + d = TARGET_VALUE) Then
combos.Add a & "," & b & "," & c & "," & d
End If
Next d
Next c
Next b
Next a
Debug.Print "calc time elapsed = " & FormattedTimeElapsed()
Debug.Print "number of combos = " & combos.Count
Dim results As Variant
ReDim results(1 To combos.Count, 1 To 4)
StartCounter
For a = 1 To combos.Count
Dim combo As Variant
combo = Split(combos.Item(a), ",")
results(a, 1) = combo(0)
results(a, 2) = combo(1)
results(a, 3) = combo(2)
results(a, 4) = combo(3)
Next a
Sheet1.Range("A1").Resize(combos.Count, 4).Value = results
Debug.Print "results to sheet1 time elapsed = " & FormattedTimeElapsed()
End Sub
I used a high-performance timer in a separate module to measure the timing. On my system the results were
calc time elapsed = 1.774 seconds
number of combos = 156849
results to sheet1 time elapsed = 3.394 minutes
The timer code module is
Option Explicit
'------------------------------------------------------------------------------
' For Precision Counter methods
'
Private Type LargeInteger
lowpart As Long
highpart As Long
End Type
Private Declare Function QueryPerformanceCounter Lib _
"kernel32" (lpPerformanceCount As LargeInteger) As Long
Private Declare Function QueryPerformanceFrequency Lib _
"kernel32" (lpFrequency As LargeInteger) As Long
Private counterStart As LargeInteger
Private counterEnd As LargeInteger
Private crFrequency As Double
Private Const TWO_32 = 4294967296# ' = 256# * 256# * 256# * 256#
'==============================================================================
' Precision Timer Controls
' from: https://stackoverflow.com/a/198702/4717755
'
Private Function LI2Double(lgInt As LargeInteger) As Double
'--- converts LARGE_INTEGER to Double
Dim low As Double
low = lgInt.lowpart
If low < 0 Then
low = low + TWO_32
End If
LI2Double = lgInt.highpart * TWO_32 + low
End Function
Public Sub StartCounter()
'--- Captures the high precision counter value to use as a starting
' reference time.
Dim perfFrequency As LargeInteger
QueryPerformanceFrequency perfFrequency
crFrequency = LI2Double(perfFrequency)
QueryPerformanceCounter counterStart
End Sub
Public Function TimeElapsed() As Double
'--- Returns the time elapsed since the call to StartCounter in microseconds
If crFrequency = 0# Then
Err.Raise Number:=11, _
Description:="Must call 'StartCounter' in order to avoid " & _
"divide by zero errors."
End If
Dim crStart As Double
Dim crStop As Double
QueryPerformanceCounter counterEnd
crStart = LI2Double(counterStart)
crStop = LI2Double(counterEnd)
TimeElapsed = 1000# * (crStop - crStart) / crFrequency
End Function
Public Function FormattedTimeElapsed() As String
'--- returns the elapsed time value as above, but in a nicely formatted
' string in seconds, minutes, or hours
Dim result As String
Dim elapsed As Double
elapsed = TimeElapsed()
If elapsed <= 1000 Then
result = Format(elapsed, "0.000") & " microseconds"
ElseIf (elapsed > 1000) And (elapsed <= 60000) Then
result = Format(elapsed / 1000, "0.000") & " seconds"
ElseIf (elapsed > 60000) And (elapsed < 3600000) Then
result = Format(elapsed / 60000, "0.000") & " minutes"
Else
result = Format(elapsed / 3600000, "0.000") & " hours"
End If
FormattedTimeElapsed = result
End Function
I have the following code. I'm passing in a cell's value from column "d" as a string (a list of days of the week, could be one day or as many as all 7 days) into this function. I'm also using the fuction to fill an array of Integers. I'm getting a type mismatch at the indicated line and I can't figure out why. Especially since I Dim the array right before filling it and stepping through it. I'd appreciate any help/explaination. Option Explicit is turned on.
Public i as Integer
Public Function ParseColumnD(cellvalue as String) as String
Dim BREdaysString() as String
Dim daysAsInt() As Integer
BREdaysString() = Split(cellvalue, ", ")
For Each i in BREdaysString()
If BREdaysString(i) = "Monday" Then '<-----Type Mismatch error here.
daysAsInt(i) = 4
ElseIf BREdaysString(i) = "Tuesday" Then
daysAsInt(i) = 5
ElseIf BREdaysString(i) = "Wednesday" Then
daysAsInt(i) = 6
ElseIf BREdaysString(i) = "Thursday" Then
daysAsInt(i) = 7
ElseIf BREdaysString(i) = "Friday" Then
daysAsInt(i) = 8
ElseIf BREdaysString(i) = "Saturday" Then
daysAsInt(i) = 9
ElseIf BREdaysString(i) = "Sunday" Then
daysAsInt(i) = 10
End If
Next
'to check to make sure the conversion from days of the week to integers is correct
'I take the int array and put it as the function's string, return it and put in another cell.
For Each i in daysAsInt()
If i = 1 Then
ParseColumnD = daysAsInt(i)
ElseIf i > 1 Then
ParseColumnD = ParseColumnD & ", " & daysAsInt(i)
End If
Next
End Funciton
In the regular module I use the function like this...
Sub MySub()
Dim BREitems as Range
Dim lastrow as Long
lastrow = Cells(Rows.count, "a").End(xlUp).Row
Set BREitems = Range("a5:a" & lastrow)
For Each i in BREitems
Cells(i.Row, "g").value = ParseColumnD(Cells(i.row, "d"))
Next
End Sub
You want a For...Next loop here to iterate over the array, using LBound and Ubound, not a For Each loop.
For i = LBound(BREdaysString) to Ubound(BREdaysString)
A For Each loop would work if you did the following:
For Each i in BREdaysString()
If i = "Monday" Then
Obviously using a better variable name then i, and Dimming as a String, as this is pretty confusing. But you can't use i as an index if you're using a For Each loop. That would be like trying to do If BREdaysString("Monday") = "Monday" Then
I don't see the point of i being Public here either. And just use Long instead of Integer .
I am currently trying to use vba to check whether a solver solution has been found already and if so generate a new one. Below is my code. There are comments to explain what is going on.
Sub Button1_Click()
Dim i As Integer
Dim j As Integer
one = 1
min_cell = Range("BF6").Value
max_cell = Range("BF7").Value
gamer = Range("BF10").Value
Dim players(1 To 200) As String 'here is the inner array
Dim lineups(1 To 200) As Variant 'here is the outer array
For i = 1 To 200
lineups(i) = players 'assigning the players array to each spot in the lineup array
Next i
If gamer = 0 Then
spot = "AZ3"
accuracy = 0.00000001
ElseIf gamer = 1 Then
spot = "BA3"
accuracy = 0.001
End If
Dim variable As Integer
lineup_quantity = Range("BF8").Value 'denotes how many different solutions the solver should generate
max_value = 1000
Dim counter As Integer
Dim count As Integer
lineup_number = 1 'denotes the current lineup number
Dim occurences(1 To 201) As Integer
For count = lineup_number To lineup_quantity 'running solver
Dim positions(1 To 201) As String
Dim cell_numbers(1 To 201)
track = 1
Worksheets("Draftkings Lookup").Range("AI1:AI500").Copy
Worksheets("Draftkings Lookup").Range("DZ1:DZ500").PasteSpecial Paste:=xlPasteValues
SolverReset
solverok setcell:=spot, maxminval:=1, bychange:="AL" + CStr(min_cell) + ":AL" + CStr(max_cell), engine:=2
SolverOptions Precision:=accuracy
solveradd cellref:="AL" + CStr(min_cell) + ":AL" + CStr(max_cell), relation:=5
solveradd cellref:=spot, relation:=1, formulatext:=max_value
solveradd cellref:="BA6:BA11", relation:=2, formulatext:="AY6"
solversolve userfinish:=True
Range("BM" + CStr(count + 1)).Value = Range(spot).Value
For counter = min_cell To max_cell
If Range("AL" + CStr(counter)).Value = 1 Then
players(track) = Range("B" + CStr(counter)).Value 'stores the result to of the solver and is working properly
positions(track) = Range("C" + CStr(counter)).Value
cell_numbers(track) = counter
track = track + 1
End If
Next counter
For counter = 1 To lineup_number 'my attempt at comparing players to each array in lineups which is not working
For i = 1 To 8
If lineups(counter)(i) <> players(i) Then
GoTo hi
End If
Next i
count = count - 1 'causes program to repeat current lineup
GoTo line
hi:
Next counter
For i = 1 To 8
lineups(lineup_number)(i) = players(i) 'adding the current players as an array in lineups since they don't match any prior lineups
Next i
line:
Worksheets("Draftkings Lookup").UsedRange.Columns("AI:AI").Calculate
Next count
End Sub
I am still getting duplicates when I run it for a large number of lineups like 40. Any help would be greatly appreciated.
I am programming a function in which I can populate my array with some values. I want to populate them because the array is way to big to fill it myself.
The array is a set of time values from which a person can choose in a dropdown (selection). The default should be 7:30 (european time) and the dates to choose from should have steps of 15 minutes. The lowest time to choose from is 5.00 and the highest is 23:00, which is a total of 18 hours. so with steps of 15 minutes that means: 18 hours x 60 minutes = 1080 minutes / 15 minutes = 72 steps of 15 minutes. So that is a total of 71 array values (counting the 0 as well).
Now the function i figured out is as follows:
Dim myArray
ReDim myArray(5)
Dim counter
For counter = 0 To UBound(myArray)
if counter = 0 then
myArray(counter) = Hour("05:00")
else
myArray(counter) = DateAdd("n",15,myArray(counter))
end if
Next
For Each item In myArray
Response.Write(item & "<br />")
Next
This bit was just for the testing:
For Each item In myArray
Response.Write(item & "<br />")
Next
Though the looping doesn't add the 15 minutes, instead it starts with the 5.00 but then keeps printing the 00:15 after the first counter, so it doesnt add the 15 minutes to its last count value.
Any clue how to fix it?
The output is as follows while testing the array with a count of 5:
5
00:15:00
00:15:00
00:15:00
00:15:00
00:15:00
Alright I figured it out myself.
I did it now by adding the value to a variable which changes in every loop to a new (the added 15 minute) value. This way it adds the 15 minutes to the latest value.
The code:
Dim myArray
ReDim myArray(5)
Dim counter
Dim BeginTijdArray
BeginTijdArray = Hour("05:00") & ":" & Minute("05:00")
For counter = 0 To UBound(myArray)
if counter = 0 then
myArray(counter) = BeginTijdArray
else
myArray(counter) = DateAdd("n",15,BeginTijdArray)
myArray(counter) = Hour(myArray(counter)) & ":" & Minute(myArray(counter))
BeginTijdArray = myArray(counter)
end if
Next
For Each item In myArray
Response.Write(item & "<br />")
Next
I believe there is a simple bug in your loop.
This line
myArray(counter) = DateAdd("n",15,myArray(counter))
should be
myArray(counter) = DateAdd("n",15,myArray(counter - 1))
I think that is all.
I want to find the index of the nth largest value in an array. I can do the following but it runs into trouble when 2 values are equal.
fltArr(0)=31
fltArr(1)=15
fltArr(2)=31
fltArr(3)=52
For i = 0 To UBound(fltArr)
If fltArr(i) = Application.WorksheetFunction.Large(fltArr, n) Then
result = i
End If
Next
n=1 ---> 3
n=2 ---> 2 (but I want this to be 0)
n=3 ---> 2
n=4 ---> 1
Uses a second array to quickly get what you want without looping through each element for every value of n
Sub test()
Dim fltArr(0 To 3)
Dim X
Dim n As Long
Dim lngPos As Long
fltArr(0) = 31
fltArr(1) = 15
fltArr(2) = 31
fltArr(3) = 52
X = fltArr
For n = 1 To 4
lngPos = Application.WorksheetFunction.Match(Application.Large(X, n), X, 0) - 1
Debug.Print lngPos
X(lngPos) = Application.Max(X)
Next
End Sub
Edit:
Public Sub RunLarge()
Dim n%, i%, result%, count%
Dim fltArr(3) As Integer
Dim iLarge As Integer
fltArr(0) = 31:
fltArr(1) = 15:
fltArr(2) = 31:
fltArr(3) = 52
n = 1
Debug.Print " n", "iLarge", "result"
While n <= 4
count% = n - 1
iLarge = Application.WorksheetFunction.Large(fltArr, n)
For i = 0 To UBound(fltArr)
If fltArr(i) = iLarge Then
result = i
count% = count% - 1
If count% <= 0 Then Exit For
End If
Next
Debug.Print n, iLarge, result
n = n + 1
Wend
End Sub
result:
n iLarge result
1 52 3
2 31 0
3 31 2
4 15 1
It's a bit "dirty" but seeing as you're in Excel...
' Create a sheet with codename wsTemp...
For i = 0 To UBound(fltArr)
wsTemp.cells(i,1) = i
wsTemp.cells(i,2) = fltArr(i)
Next
with wsTemp
.range(.cells(1,1),.cells(i,2)).sort(wsTemp.cells(1,2),xlDescending)
end with
Result = wsTemp.cells(n,1)
Then you could also expand the sort to "sort by value then by index" if you wanted to control the "which of two equal 2nds should i choose" thing...
Perhaps this:
Public Sub RunLarge()
Dim fltArr() As Variant, X As Long
fltArr = Array(31, 15, 31, 52) 'Create the array
For X = 1 To 4 'Loop the number of large values you want to index
For i = LBound(fltArr) To UBound(fltArr) 'Loop the array
If fltArr(i) = Application.WorksheetFunction.Large(fltArr, 1) Then 'Find first instance of largest value
result = i
fltArr(i) = -9999 'Change the value in the array to -9999
Exit For
End If
Next
Debug.Print result
Next
End Sub
As it finds the first instance of the large number it replaces it with -9999 so on the next sweep it will pick the next instance of it.
Here's code for finding the nth largest item in collection. All you need to do is to write a function that would return it's index.
Sub testColl()
Dim tempColl As Collection
Set tempColl = New Collection
tempColl.Add 57
tempColl.Add 10
tempColl.Add 15
tempColl.Add 100
tempColl.Add 8
Debug.Print largestNumber(tempColl, 2) 'prints 57
End Sub
and the function itself, the easiest I could come up with.
Function largestNumber(inputColl As Collection, indexMax As Long)
Dim element As Variant
Dim result As Double
result = 0
Dim i As Long
Dim previousMax As Double
For i = 1 To indexMax
For Each element In inputColl
If i > 1 And element > result And element < previousMax Then
result = element
ElseIf i = 1 And element > result Then
result = element
End If
Next
previousMax = result
result = 0
Next
largestNumber = previousMax
End Function