This is a console application which generates a times table with user input by asking the user to input rows and columns. I get two big errors in this code:
Value of type '1-dimensional array of 1-dimensional array of Integer' cannot be converted to '1-dimensional array of Integer' 'because '1-dimensional array of Integer' is not derived from 'Integer'
and
'jaggedArrayArray' is not declared. It may be inaccessible due to its protection level.
After some research online, I have come across two big concepts - Deep Copy and Shallow Copy - which I am still learning. I think that my main problem has to do with Sub arrayPopulateJ:
Sub arrayPopulateJ(ByVal jaggedArray() As Integer, ByVal columns As Integer, ByVal rows As Integer)
Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim mult(columns) As Integer
'Populates rows in jagged array
For i = 0 To rows
jaggedArray(i) = (i + 1)
Next
'Populates columns in arrays
For i = 0 To rows
For j = 0 To columns
For k = 0 To columns
mult(k) = (j + 1) * (k + 1)
Next
Next
jaggedArray(i) = mult(columns)
Next
End Sub
If you look at the line jaggedArray(i) = mult(columns) I think I am doing what is called a shallow copy and it is making this whole thing not work. What I want to happen is I want to be able to use jaggedArray as a 1D array and put 1D arrays into its elements (in my code that would be mult(columns)). I am still new to programming and VB and I am not sure how to do this. I thought that VB would be a high enough language that the flow of logic would work this way. But as I know now that is not the case. So what can I do to pass an whole array into a array and get this to work?
FULL CODE:
Module Module1
Sub Main()
'Declarations
Dim awns As Char
Dim switchOption As Integer
Dim columns As Integer
Dim rows As Integer
Dim regularArray(,) As Integer = New Integer(,) {}
Dim jaggedArray()() As Integer = New Integer(rows)() {} 'Problem here
Dim topArray(columns) As Integer
Dim sideArray(rows) As Integer
'Starting Prompts
Console.WriteLine("Hello this program will create a times table with")
Console.WriteLine("user inputs in terms of rows and columns.")
Console.WriteLine("Pick between these two options.")
Console.WriteLine("Option 1: Times table with a regular array.")
Console.WriteLine("Option 2: Times table with a jagged array.")
Do
Console.Write("Which option do you want? ")
switchOption = Console.ReadLine
Console.WriteLine("How many columns do you want? ")
columns = Console.ReadLine
columns = columns - 1
Console.WriteLine("How many rows do you want? ")
rows = Console.ReadLine
rows = rows - 1
Console.Write(vbNewLine)
'ReDim's
ReDim regularArray(columns, rows)
ReDim jaggedArray(rows)
ReDim topArray(columns)
ReDim sideArray(rows)
Select Case switchOption
Case 1
'Array populations
arrayPopulate(regularArray, columns, rows)
singlePopulate(topArray, columns)
singlePopulate(sideArray, rows)
Dim i As Integer
Dim j As Integer
Console.Write(" ")
For j = 0 To columns
Dim top As String = topArray(j)
Console.Write(top.PadLeft(3) + ": ")
Next
Console.Write(vbNewLine)
For j = 0 To rows
Dim side As String = sideArray(j)
Console.Write(side.PadLeft(3) + ": ")
For i = 0 To columns
Dim num As String = regularArray(i, j)
Console.Write(num.PadLeft(3) + ": ")
Next
Console.Write(vbNewLine)
Next
Case 2
'Array populations
arrayPopulateJ(jaggedArray, columns, rows) 'Problem here
singlePopulate(topArray, columns)
singlePopulate(sideArray, rows)
Dim i As Integer
Dim j As Integer
Console.Write(" ")
For j = 0 To columns
Dim top As String = topArray(j)
Console.Write(top.PadLeft(3) + ": ")
Next
Console.Write(vbNewLine)
For j = 0 To rows
Dim side As String = sideArray(j)
Console.Write(side.PadLeft(3) + ": ")
Dim num As String = jaggedArrayArray(j) 'Problem here
Console.Write(num.PadLeft(3))
Console.Write(vbNewLine)
Next
End Select
Console.WriteLine("Do you want to run again y/n?")
awns = Console.ReadLine()
Loop Until awns = "n"
End Sub
Sub arrayPopulateJ(ByVal jaggedArray() As Integer, ByVal columns As Integer, ByVal rows As Integer)
Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim mult(columns) As Integer
ReDim mult(columns)
'Populates rows in jagged array
For i = 0 To rows
jaggedArray(i) = (i + 1)
Next
'Populates columns in arrays
For i = 0 To rows
For j = 0 To columns
For k = 0 To columns
mult(k) = (j + 1) * (k + 1)
Next
Next
jaggedArray(i) = mult(columns)
Next
End Sub
Sub arrayPopulate(ByVal regularArray(,) As Integer, ByVal columns As Integer, ByVal rows As Integer)
'Local Declarations
Dim i As Integer
Dim j As Integer
Dim mult As Integer
For i = 0 To rows
For j = 0 To columns
mult = (i + 1) * (j + 1)
regularArray(j, i) = mult
Next
Next
End Sub
Sub singlePopulate(ByVal topArray() As Integer, ByRef count As Integer)
'Local Declarations
Dim i As Integer
Dim pop As Integer
For i = 0 To count
pop = (i + 1)
topArray(i) = pop
Next
End Sub
End Module
There is no "deep" or "shallow" copy issue here. That's a red herring.
Your first problem was that you had jaggedArrayArray in your code, but the variable was declared as jaggedArray.
The next problem that arrayPopulateJ was expecting the first parameter to be of type Integer() when it should have been Integer()().
Fixing both of this it was then just an easy matter of writing arrayPopulateJ to be:
Sub arrayPopulateJ(ByVal jaggedArray()() As Integer, ByVal columns As Integer, ByVal rows As Integer)
For i = 0 To rows
Dim column(columns) As Integer
jaggedArray(i) = column
For j = 0 To columns
jaggedArray(i)(j) = (i + 1) * (j + 1)
Next
Next
End Sub
I also cleaned up arrayPopulate to be:
Sub arrayPopulate(ByVal regularArray(,) As Integer, ByVal columns As Integer, ByVal rows As Integer)
For i = 0 To rows
For j = 0 To columns
regularArray(j, i) = (i + 1) * (j + 1)
Next
Next
End Sub
I ran your code at that point and it worked.
Related
I have 2 arrays that I want to combine into a single array of all possible combinations. I then need to loop through all of the combinations and popular arguments for a function. My arrays are not equal in size, and my attempts so far have resulted in a combined array only having 1 pair of values. This is VBA in PowerPoint, not Excel, if that makes a difference to available syntax.
How can I go from this:
arrayColor = Array("Blue","Green","Red")
arraySize = Array("XS","S","M","L","XL")
To this:
arrayCombo(0,0) = "Blue"
arrayCombo(0,1) = "XS"
arrayCombo(1,0) = "Blue"
arrayCombo(1,1) = "S"
...
arrayCombo(15,0) = "Red"
arrayCombo(15,1) = "XL"
And then use a loop to call each pair of values and populate argument values. This code just to illustrate the concept; it's certainly not legit. Pretty sure I need a nested loop here?
For i = 0 To UBound(arrayCombo(i))
nextSubToFire(color, size)
Next i
This is what I've got so far, but it only results in a single pair in my combined array. It's based on this question, but I think I'm either missing something or the sole answer there isn't quite correct. I've looked at other similar questions, but can't wrap my head around doing this with an array compiled in the code rather than the other examples all tailored to Excel.
Option Explicit
Dim arrayColorSize, arrayCombo
Sub CoreRoutine()
Dim arrayColor, arraySize
arrayColor = Array("Blue","Green","Red")
arraySize = Array("XS","S","M","L","XL")
arrayColorSize = Array(arrayColor, arraySize)
arrayCombo = Array(0, 0)
DoCombinations (0)
Dim a As Integer
Dim b As Integer
'For loop comes next once I figure out how to populate the full arrayCombo
End Sub
Sub DoCombinations(ia)
Dim i
For i = 0 To UBound(arrayColorSize(ia)) ' for each item
arrayCombo(ia) = arrayColorSize(ia)(i) ' add this item
If ia = UBound(arrayColorSize) Then
Else
DoCombinations (ia + 1)
End If
Next i
End Sub
Using the Locals window, I see arrayCombo exists, but it only has 1 pair of values in it, which is the last set of pairing options. I see that arrayColorSize has the 2 array sets as I'd expect, so I suspect the DoCombinations sub is missing something.
Any guidance much appreciated!
One way of doing this is to combine the two 1D arrays into a 2D array with 2 columns (as in your example):
Private Function Combine1DArrays(ByRef arr1 As Variant, ByRef arr2 As Variant) As Variant
If GetArrayDimsCount(arr1) <> 1 Or GetArrayDimsCount(arr2) <> 1 Then
Err.Raise 5, "Combine1DArrays", "Expected 1D arrays"
End If
'
Dim count1 As Long: count1 = UBound(arr1) - LBound(arr1) + 1
Dim count2 As Long: count2 = UBound(arr2) - LBound(arr2) + 1
Dim i As Long, j As Long, r As Long
Dim result() As Variant
'
ReDim result(0 To count1 * count2 - 1, 0 To 1)
r = 0
For i = LBound(arr1) To UBound(arr1)
For j = LBound(arr2) To UBound(arr2)
result(r, 0) = arr1(i)
result(r, 1) = arr2(j)
r = r + 1
Next j
Next i
Combine1DArrays = result
End Function
Public Function GetArrayDimsCount(ByRef arr As Variant) As Long
Const MAX_DIMENSION As Long = 60
Dim dimension As Long
Dim tempBound As Long
'
On Error GoTo FinalDimension
For dimension = 1 To MAX_DIMENSION
tempBound = LBound(arr, dimension)
Next dimension
FinalDimension:
GetArrayDimsCount = dimension - 1
End Function
You can use it like this for example:
Sub CoreRoutine()
Dim arrayColorSize As Variant
Dim i As Long
Dim color As String
Dim size As String
'
arrayColorSize = Combine1DArrays(Array("Blue", "Green", "Red") _
, Array("XS", "S", "M", "L", "XL"))
For i = LBound(arrayColorSize, 1) To UBound(arrayColorSize, 1)
color = arrayColorSize(i, 0)
size = arrayColorSize(i, 1)
NextSubToFire color, size
Next i
End Sub
Sub NextSubToFire(ByVal color As String, ByVal size As String)
Debug.Print color, size
End Sub
I am running into a problem, Although very simple but stuck up, I have a string from a cell, I split the string into characters using Mid function and store it into an array. Now I want to print the array to a different range but I am unable to do it. I've tried many different codes but all in vein.
please help.
My Code is as
Option Base 1
Function Takseer(Rg As Variant)
Dim NewArray() As Variant
Dim StrEx As String
Dim k, l, m As Integer
StrEx = Rg
StrEx = WorksheetFunction.Substitute(StrEx, " ", "")
m = Len(StrEx)
For k = 1 To m
ReDim Preserve NewArray(1 To m)
NewArray(k) = Mid(StrEx, k, 1)
Next k
Range("C1:C12") = NewArray
End Function
You have to transpose the array to put values in a column.
Option Explicit
Option Base 1
Sub test()
Call Takseer("ABCDEFGHUIJKL")
End Sub
Function Takseer(StrEx As String)
Dim NewArray() As Variant, s As String, m As Integer, k As Integer
s = Replace(StrEx, " ", "")
m = Len(s)
If m = 0 Then Exit Function
ReDim NewArray(m)
For k = 1 To m
NewArray(k) = Mid(s, k, 1)
Next k
' in a row
Sheet1.Range("C1").Resize(1, m) = NewArray
' in a column
Sheet1.Range("C2").Resize(m, 1) = WorksheetFunction.Transpose(NewArray)
End Function
Assuming the array you obtain is "Apple", "Orange", "Grape", "Durian", in order to write into worksheet you cannot directly call the variant. One way to write the value is to first get the length of your variant, then write the value from array starting from index 0, here is how I perform you expectation:
Sub test1()
Dim NewArray() As Variant
Dim i As Long, arrayLoop As Long
Dim StrEx As String
Dim k, l, m As Integer
StrEx = "Hello today is my first day"
StrEx = WorksheetFunction.Substitute(StrEx, " ", "")
m = Len(StrEx)
For k = 0 To m - 1
ReDim Preserve NewArray(m - 1)
NewArray(k) = Mid(StrEx, k + 1, 1)
Next k
i = UBound(NewArray) - LBound(NewArray) + 1
For arrayLoop = 0 To i - 1
Sheet1.Range("A" & arrayLoop + 1).Value = NewArray(arrayLoop)
Next
End Sub
Please take note when perform array loop, you have to minus the length by 1, else it will be out of range, the reason is that array index always start from zero based (0)
And check the post for how to obtain length of array Get length of array?
Some problems with your function:
A formula returns a value. It is not used to alter other properties/cells of a worksheet.
Hence you should set your results to the function; not try to write to a range
Dim k, l, m As Integer only declares m as Integer, k and l are unspecified so they will be declared as a variant.
The constructed array will be horizontal. If you want the results vertical, you need to Transpose it, or create a 2D array initially.
Option Base 1 is unnecessary since you explicitly declare the lower bound
Assuming you want to use this function on a worksheet, TestIt sets things up.
Note2: The formula on the worksheet assumes you have Excel with dynamic arrays. If you have an earlier version of Excel, you will need to have a different worksheet formula
See your modifed function and TestIt:
Modified with Transpose added to worksheet formula
Option Explicit
Function Takseer(Rg As Variant)
Dim NewArray() As Variant
Dim StrEx As String
Dim k As Long, l As Long, m As Long
StrEx = Rg
StrEx = WorksheetFunction.Substitute(StrEx, " ", "")
m = Len(StrEx)
For k = 1 To m
ReDim Preserve NewArray(1 To m)
NewArray(k) = Mid(StrEx, k, 1)
Next k
Takseer = NewArray
End Function
Sub TestIt()
[a1] = "abcdefg"
[c1].EntireColumn.Clear
[c1].Formula2 = "=Transpose(Takseer(A1))"
End Sub
Modified to create 2d vertical array
can't really use redim preserve on this array. And I prefer to avoid it anyway because of the overhead
Option Explicit
Function Takseer(Rg As Variant)
Dim NewArray() As Variant, col As Collection
Dim StrEx As String
Dim k As Long, l As Long, m As Long
StrEx = Rg
StrEx = WorksheetFunction.Substitute(StrEx, " ", "")
m = Len(StrEx)
Set col = New Collection
For k = 1 To m
col.Add Mid(StrEx, k, 1)
Next k
ReDim NewArray(1 To col.Count, 1 To 1)
For k = 1 To col.Count
NewArray(k, 1) = col(k)
Next k
Takseer = NewArray
End Function
Sub TestIt()
[a1] = "abcdefg"
[c1].EntireColumn.Clear
[c1].Formula2 = "=Takseer(A1)"
End Sub
Note:
TestIt is merely to test the function. You should enter the appropriate formula yourself, either manually or programmatically, into the destination range.
If you do not have dynamic arrays, then you would need to enter an array formula into the destination range; or a formula using the INDEX function to return each element of the array.
In TestIt, you might change the line that puts the formula onto the worksheet to Range(Cells(1, 3), Cells(Len([a1]), 3)).FormulaArray = "=Takseer(a1)", but, again, it is anticipated that you would be entering the correct formula onto your worksheet manually or programmatically anyway.
Hello I am having a hard time with arrays in visual basic. This is a simple console application (I am trying to get the hang of the syntax before I move on to gui) and all this program does is use two types of arrays jagged and the regular type. This console application is a times table generator i.e enter in 5 columns and 5 rows to make a 5X5 times table. The program is not finished yet and I know the times table wont generate right with this code so far but what I need help with is how to populate arrays in VB. Where my problem is in this sub
SUB:
Sub arrayPopulate(ByVal regularArray(,) As Integer, ByVal columns As Integer, ByVal rows As Integer)
Dim i As Integer
Dim j As Integer
Dim mult As Integer
For i = 0 To rows
For j = 0 To columns
mult = (i + 1) * (j + 1)
regularArray(j, i) = mult
Next
Next
End Sub
Specifically the line regularArray(j, i) = mult I would have thought it be simple the arrays element is set = to what ever the mult is and the for loops would cover the 2d array. What am I doing wrong and how could I fix it or do it better?
FULL CODE:
Module Module1
Sub Main()
'Declarations
Dim awns As Char
Dim switchOption As Integer
Dim columns As Integer
Dim rows As Integer
Dim regularArray(columns, rows) As Integer
'Starting Prompts
Console.WriteLine("Hello this program will create a times table with")
Console.WriteLine("user inputs in terms of rows and columns.")
Console.WriteLine("Pick between these two options.")
Console.WriteLine("Option 1: Times table with a regular array.")
Console.WriteLine("Option 2: Times table with a jagged array.")
Do
Console.Write("Which option do you wnat? ")
switchOption = Console.ReadLine
Console.WriteLine("How many columns do you wnat? ")
columns = Console.ReadLine
columns = columns - 1
Console.WriteLine("How many rows do you wnat? ")
rows = Console.ReadLine
rows = rows - 1
Select Case switchOption
Case 1
arrayPopulate(regularArray, columns, rows)
Dim i As Integer
Dim j As Integer
For j = 0 To rows
For i = 0 To columns
Console.WriteLine("{0}: ", regularArray(i, j))
Next
Next
Case 2
Console.WriteLine("Test")
End Select
Console.WriteLine("Do you want to run again y/n?")
awns = Console.ReadLine()
Loop Until awns = "n"
End Sub
Sub arrayPopulate(ByVal regularArray(,) As Integer, ByVal columns As Integer, ByVal rows As Integer)
Dim i As Integer
Dim j As Integer
Dim mult As Integer
For i = 0 To rows
For j = 0 To columns
mult = (i + 1) * (j + 1)
regularArray(j, i) = mult
Next
Next
End Sub
End Module
Where you declared Dim regularArray(columns, rows) As Integer, it makes use of the values of columns and rows as they were at the time; in this case, they were both zero, and hence regularArray is a two-dimensional array having one element - namely, regularArray(0, 0). Use ReDim to change the size of its dimensions. E.g., put this just before the Select Case switchOption:
ReDim regularArray(columns, rows)
See http://msdn.microsoft.com/en-us/library/w8k3cys2.aspx for details.
I'm trying to cumulate the sums of values in an excel column of 4 values dimension (4,1).
So, I constructed the code below. For the first row in a column on the side Result, it is supposed to hold the same value as in the original Array.
But then, once it is greater than the first row, it is supposed to get the previous element of result (i-1) and add to it the current column element (i).
VBA is telling me that the subscript is out of range :/ and I cant figure out why... so I dont even know if my code does what I want.
Sub CumulativeSum()
Dim i As Integer
Dim j As Integer
Dim rColumn() As Variant
Dim result() As Variant
ReDim result(1 To 4)
rColumn = Worksheets("Sheet1").Range("E1:E4").Value2
For i = 1 To 4
result(1) = rColumn(1, 1)
For j = 2 To 3
result(j) = rColumn(j, 1) + result(j - 1)
Next j
Next i
Dim dest As Range
Set dest = Worksheets("Sheet1").Range("F1")
dest.Resize(4, 1).Value = result
End Sub
Sub CumulativeSum()
Dim dest As Range
Dim i As Integer
Dim j As Integer
Dim rColumn() As Variant
Dim result() As Variant
ReDim result(1 To 4)
rColumn = Worksheets("Sheet1").Range("E1:E4").Value2
result(1) = rColumn(1, 1)
For j = 2 To 4
result(j) = rColumn(j, 1) + result(j - 1)
Next j
Set dest = Worksheets("Sheet1").Range("F1")
dest.Resize(4, 1).Value = Application.Transpose(result)
End Sub
Don't have enough rep to add a comment but.. the reason why you're getting an error is because the Syntax for Cells is Cells([Row],[Column]). You're typing it in as Cells([Column],[Row]).
Try Range(Cells(1, 5), Cells(4, 5)) instead.
I don't understand why for each loop in vba doesn't return the good number of element when i use dynamic array.
For exemple, my array size is 4, and i have 5 iteration in for each loop ...
Public Sub test()
Dim t_direction() As String
Dim t_nextDirection() As String
Dim myDirection As Variant
Dim test As Integer
Var = 0
ReDim t_direction(4)
t_direction(0) = "N"
t_direction(1) = "S"
t_direction(2) = "E"
t_direction(3) = "W"
t_nextDirection = randomizeArray(t_direction)
For Each myDirection In t_nextDirection
Var = Var + 1
Next myDirection
MsgBox (UBound(t_nextDirection))
MsgBox (Var)
End Sub
Public Function randomizeArray(ByVal t_array As Variant) As String()
Dim i As Integer
Dim j As Integer
Dim tmp As String
Dim numItems As Integer
numItems = UBound(t_array) - 1
' Randomize the array.
For i = 0 To numItems
' Pick a random entry.
j = Rand(0, numItems)
' Swap the numbers.
tmp = t_array(i)
t_array(i) = t_array(j)
t_array(j) = tmp
Next i
'MsgBox (UBound(t_array))
randomizeArray = t_array
End Function
Public Function Rand(ByVal Low As Long, _
ByVal High As Long) As Integer
Rand = Int((High - Low + 1) * Rnd) + Low
End Function
At the moment you are creating a 5 element array with
ReDim t_direction(4)
as the first element occurs as t_direction(0)
You should either
create a 4 element array ReDim t_direction(3) (ie 0 to 3) and then use numItems consistent with that, or
create a 4 element array ReDim t_direction with a base of 1 (ie 1 to 4) and then use numItems consistent with that (ie numItems = UBound(t_array)). The Option Base 1 below forces the first element to be 1 (which is then ensured anyow by using ReDim t_direction(1 To 4)
The code below uses the later approach. It returns 4 and 4 rather than your current 4 and 5
Option Base 1
Public Sub test()
Dim t_direction() As String
Dim t_nextDirection() As String
Dim myDirection As Variant
Dim test As Integer
Var = 0
ReDim t_direction(1 To 4)
t_direction(1) = "N"
t_direction(2) = "S"
t_direction(3) = "E"
t_direction(4) = "W"
t_nextDirection = randomizeArray(t_direction)
For Each myDirection In t_nextDirection
Var = Var + 1
Next myDirection
MsgBox (UBound(t_nextDirection))
MsgBox (Var)
End Sub
Public Function randomizeArray(ByVal t_array As Variant) As String()
Dim i As Integer
Dim j As Integer
Dim tmp As String
Dim numItems As Integer
numItems = UBound(t_array)
' Randomize the array.
For i = 1 To numItems
' Pick a random entry.
j = Rand(1, numItems)
' Swap the numbers.
tmp = t_array(i)
t_array(i) = t_array(j)
t_array(j) = tmp
Next i
'MsgBox (UBound(t_array))
randomizeArray = t_array
End Function
Public Function Rand(ByVal Low As Long, _
ByVal High As Long) As Integer
Rand = Int((High - Low + 1) * Rnd) + Low
End Function
ReDim t_direction(4) actually declares t_direction as 0 To 4
Its better to be explicit:
ReDim t_direction(0 To 3)
In the absence of a specified lower bound (using the To clause), then the default lower bound is used.
This default can be set to 0 or 1 by using Option Base {0|1} at module level.
In the absence of Option Base then the default default is 0
Notes:
In VBA you are not limited to 0 or 1 as the lower bound, you can use any value you want.
To iterate over an array use
For i = LBound(arr) To UBound(arr)
To calculate the number of items in an array use
numItems = UBound(arr) - LBound(arr) + 1
This way you are not making any assumptions on what the lower bound is