I'm trying to run a VBA application using a loop and using variables whose names depends on where in the loop I am. Specifically something like
Dim i As Integer
i = 1
Dim varname() As String
while i < 50
varname(i) = asdasd
i = i + 1
Wend
Somehow it can't read varname(i) or whatever. It reports subscript out of range.
I have no idea what the problem is, can someone helt me perhaps?
You need to give your array a capacity first.
Sub max()
Dim i As Integer
i = 1
Dim varname() As String
ReDim varname(49) '<---- There
While i < 50
varname(i) = asdasd
i = i + 1
Wend
End Sub
This is a good resource for VBA arrays:
http://msdn.microsoft.com/en-us/library/office/aa164778(v=office.10).aspx
Related
gentleman! I am having trouble with figuring out a way to define the number of elements as variable when listing all possible combinations. I have a hard coded example of this where number of elements = 3
'Declare variables
Dim a as long
Dim b as Long
Dim C as Long
Dim ElementsArray as variant
'Array
ElementsArray = array("1400","1900","2400")
'Loop through combinations
for a = lbound(ElementsArray) to ubound(ElementsArray)
for B= lbound(ElementsArray) to ubound(ElementsArray)
for c = lbound(ElementsArray) to ubound(ElementsArray)
debug.print(ElementsArray(a) & " - " & ElementsArray(b) & " - " & ElementsArray(c))
next c
next b
next a
But What I am looking for is a code in which perhaps the number of nested For loops is a variable or some other ways to permutate through all possible combinations. Please help solve this problem.
Here is an example of a recursive implementation. Just be warned that you shouldn't make your array too large as you will get n to the power of n solutions - for 4 elements, that's 256, for 5 elements 3'125, for 6 you get 46'656 and for 7 already 823'543 - don't complain if the program takes a long time to execute. And of course you need a way to do something with every permutation.
Option Explicit
Sub test()
Dim ElementsArray As Variant
ElementsArray = Array("1400", "1900", "2400")
ReDim SolutionArray(LBound(ElementsArray) To UBound(ElementsArray))
recursion ElementsArray, SolutionArray, LBound(ElementsArray)
End Sub
Sub recursion(elements, solution, level As Long)
Dim i As Long
For i = LBound(elements) To UBound(elements)
solution(level) = elements(i)
If level = UBound(elements) Then
Debug.Print Join(solution, " - ")
Else
recursion elements, solution, level + 1
End If
Next i
End Sub
Update: This is the result:
Update
Still not sure if I understand. The following code will create a list of n-Tupel out of an array of values.
In the example (test), we have an array of 4 values and set n to 3 (defined as constant).
Sub test()
Const n = 3
Dim ElementsArray As Variant
ElementsArray = Array("1400", "1900", "2400", "9999")
ReDim SolutionArray(0 To n - 1)
recursion ElementsArray, SolutionArray, LBound(ElementsArray)
End Sub
Sub recursion(elements, solution, level As Long)
Dim i As Long
For i = LBound(elements) To UBound(elements)
solution(level) = elements(i)
If level = UBound(solution) Then
Debug.Print Join(solution, " - ")
Else
recursion elements, solution, level + 1
End If
Next i
End Sub
I'm trying to create a For Loop that only selects some values but can't work out the Syntax, or if it's even possible?
I want it to be like
Dim i As Integer
For i = 1,3,8,15 Then
Do something
Next i
Any Ideas?
Try
Sub Demo()
Dim indexArr As Variant
Dim i As Long
indexArr = Array(1, 3, 8, 15)
For i = LBound(indexArr) To UBound(indexArr)
Debug.Print indexArr(i)
Next i
End Sub
You can do following:
Dim i as Variant
Dim iArray as Variant
iArray=Array(1,3,8,15)
For Each i In iArray
'Do Something
Next i
Cheers
You can't.
If there is a pattern you can use Step
For i = 1 to 15 Step 2
Which will do 1,3,5,7,...
In this case since there is no pattern you will need to add an If or Select Case:
Dim i As Integer
For i = 1 to 15 Then
Select Case i
Case 1,3,8,15
'Do Something
End Select
Next i
You can use if statement:
Dim i As Integer
For i = 1 To 15 Then
If i = 1 or i = 3 or i = 8 or i = 15 Then
'Do something
End If
Next i
Hope this help.
This solution combines elements from the array solution above with the ease of use that I inferred was wanted by the OP. The array method requires looping through the array elements and then converting the array element back into a variable. This method skips 2 bits. Firstly, it skips the complex step logic of stepping through array elements. Secondly, it skips the step of converting an array element back into a usable variable.
Dim number_list as collection
set number_list = new collection
number_list.add 1
number_list.add 3
number_list.add 8
number_list.add 15
for each number in number_list
'Do something with number
Next number
This is even a simplification of the routine of Kedimir:
Original question:
Dim i As Integer
For i = 1,3,8,15 Then
' Do something
Next i
Solution:
Dim i As Variant
For Each i In Array(1, 3, 8, 15)
' Do something
Next i
For your information: the Variant is needed for the For Each loop, don't try to modify it into an integer, or the For Each loop won't be accepted.
I don't think that a more resembling solution is possible :-)
I've built a name range for 20 cells so that I can input a new list of projects which will vary from 1 to 20. I want to write a macro so that it reads the number of projects and creates the correct number of tabs, and names the tab after the project name listed in the named range. I've done all of this except I can't get the countA function to work. The named range is csCount. if I change the For loop to the correct number in one instance (if I put 7 because right now I have 7 projects) the loop and macro are correct. I want to make it more dynamic using the countA. Thank you very much for the help.
Sub generateDepartments()
Dim tabs As Integer
Dim sName As String
Dim i As Integer
Dim j As Integer
Dim csCount As Variant
tabs = Application.CountA(csCount)
j = 5
i = tabs
For i = 2 To Application.CountA(csCount)
Worksheets("Input").Activate
sName = Cells(j, 3).Value
Worksheets.Add(after:=Worksheets(i)).Name = sName
j = j + 1
Next
End Sub
First you need to create a variable to access your named range: Set csCount = ActiveWorkbook.Names("csCount").RefersToRange or Set csCount = ActiveSheet.Range("csCount")
Then use Application.WorksheetFunction.CountA(csCount)
Also, is better to define as a Range instead of Variant Dim csCount As Range
I have been trying to get this VBA script to work to automate a task, but I cannot get it to work.
Basically, I have a big task list in excel with multiple columns and over 1000 Rows. It contains the task, who it is assigned to, and if it is open or closed.
In column H is who it assigned to and column N is whether the task is opened or closed.
I am trying to search by last name and if it is OPEN to add one to the counter. The end goal is to get a total count of how many open tasks a person has. Also, some of the cells in column N (task status) has extra text like comments, etc. I am sure that a InStr Function to search for the one word within the Cell would work better, but I cannot figure it out...
here is my code
Sub statuscount()
Dim tasksheet As Worksheet
Dim simons_count As Integer
Set tasksheet = ThisWorkbook.Sheets("tasks")
lr = tasksheet.Cells(Rows.Count, 1).End(xlUp).Row
For x = 5 to lr
If tasksheet.Cells(x, 8) = "Simons" And tasksheet.Cells(x, 14) = "OPEN" Then
simons_count = simons_count + 1
End If
Next x
tasksheet.Range("$O$5").Value = simons_count
End Sub
Thanks for the help!
Using If/And gets tricky in VBA, you're better off nesting two if statements:
For x = 5 to lr
If tasksheet.Cells(x, 8) = "Simons" Then
If InStr(tasksheet.Cells(x, 14).Value, "OPEN") > 0 Then
simons_count = simons_count + 1
End If
End If
Next x
This is a more general function. Insert a module and past the below code in it. Than you can use the function just like any other Excel built-in function
Function LastNamecounter(lastName As String, status As String) As Long
LastNamecounter = 0
Dim tasksheet As Worksheet
Set tasksheet = ThisWorkbook.Sheets("tasks")
lr = tasksheet.Cells(Rows.Count, 1).End(xlUp).Row
For i = 5 To lr
If InStr(tasksheet.Cells(i, 8).Value, lastName) <> 0 And InStr(tasksheet.Cells(i, 14).Value, status) <> 0 Then
LastNamecounter = LastNamecounter + 1
End If
Next i
End Function
I've been working on this for two days and thrashing around and here's where I am now.
Private Function SetColumns(sRptVer As String, iColumns() As Integer)
If sRptVer = "Q1" Then
iColumns(1) = 5 '<- Subscript out of range error here Hovering shows _
'"iColumns(1)=<Subscript out of range>"
iColumns(2) = 6
iColumns(3) = 7
iColumns(4) = 17
End If
If sRptVer = "Q2" Then
iColumns(1) = 5
iColumns(2) = 6
iColumns(3) = 7
iColumns(4) = 8
iColumns(5) = 9
iColumns(6) = 10
iColumns(7) = 17
End If
SetColumns = iColumns()
End Function
Private Sub Test2()
Dim iColValue() As Integer
Dim sRptVer As String
Dim iColumns() As Integer
sRptVer = "Q1"
iColValue() = SetColumns(sRptVer, iColumns())
For i = 1 To 10
Debug.Print iColValue(i)
Next i
End Sub
The goal is to be able to be able to pass a string designating the quarter and return an array that will serve to set the columns I will iterate over to get values out of an Excel spreadsheet. (I'm pulling data from Excel into Access).
I've tried starting with iColumns(0). No difference.
NOTE: Access vba does not like to set arrays like so iColumns()={1,2,3,4}. It balks at the "{}".
Access vba doesn't seem to like "ReDim" either, I get a compile error: Syntax error when I "redim icolumns() as Integer" in the SetColumns function.
Thanks in advance.
You don't need two separate arrays in your calling procedure to do what you want. Try this instead:
Private Function SetColumns(sRptVer As String) As Integer()
Dim iColumns() As Integer
Select Case sRptVer
Case "Q1"
ReDim iColumns(1 To 4)
iColumns(1) = 5
iColumns(2) = 6
iColumns(3) = 7
iColumns(4) = 17
Case "Q2"
ReDim iColumns(1 To 7)
iColumns(1) = 5
iColumns(2) = 6
iColumns(3) = 7
iColumns(4) = 8
iColumns(5) = 9
iColumns(6) = 10
iColumns(7) = 17
End Select
SetColumns = iColumns
End Function
Private Sub Test2()
Dim sRptVer As String
Dim iColValue() As Integer
sRptVer = "Q1"
iColValue() = SetColumns(sRptVer)
Dim i As Integer
For i = LBound(iColValue) To UBound(iColValue)
Debug.Print iColValue(i)
Next i
End Sub
I made a few other changes:
I used ReDim to dynamically size the array based on the parameter passed to SetColumns.
I passed both lower and upper bounds to the array in the ReDim statement. Arrays in VBA are zero-bound by default*, so ReDim iColumns(4) is actually equivalent to ReDim iColumns(0 To 4).
Because we no longer know the size of the array ahead of time, I use the LBound() and UBound() functions to walk the array.
I changed your two If statements to a Select...Case statement. Based on your limited sample code that you provided this seemed appropriate. If your actual code is more complex, it may not be.
*NOTE: Unfortunately, you can change the starting array base in VBA using the Option Base statement. Please don't do this. Ever. You will only confuse yourself and others. If you want a specific array to start from 1 then be explicit when you Dim or ReDim the array as I showed in my example.