redimension multidimensional arrays in Excel VBA - excel

Take a look at the following code. What my problem is is that I can't figure out how to redimension the n integer and the b integer. What I'm doing is the array sent1 is already working and it is populated with about 4 sentences. I need to go through each sentence and work on it but I'm having trouble.
dim sent1()
dim sent2()
dim n as integer, b as integer, x as integer
dim temp_sent as string
b = 0
For n = 1 to ubound(sent1)
temp_sent = sent1(n)
for x = 1 to len(temp_sent1)
code
if a then
b = b + 1
'**THIS IS THE PART OF THE CODE THAT IS NOT WORKING**
redim preserve sent2(1 to ubound(sent1), b)
sent2(n,b) = [code]
next
next

There are two issues in your code:
When you Dim an array without specifying the lower bound it will by default be 0 based (unless you have specified Option Base 1). When explicitly specified, lower bound can be any number, not just 0 or 1
For a multi dimensioned array, Redim Preserve can only change the last dimension, and then only the upper bound.
In general, I find it better to always specify Lower and Upper bounds, eg
Redim MyArray(1 to 10, 0 to 99)

Is there any specific reason why you want to / must use arrays?
If not, I'd suggest using collections instead. You can also have nested collections, e.g.
Dim dimension1 As New Collection
Dim dimension2 AS New Collection
dimension1.Add dimension2
etc.
That way, you won't have to worry about increasing dimensions manually at all. If you need to convert it back to a 2D Array, you can do sth like this in the end
Dim item AS Variant
Dim subCollection AS Collection
Dim nRows AS Integer
Dim nCols AS integer
' assuming "col" is your jagged collection
nRows = col.Count
For Each item in col
If TypeOf item is Collection
Set subCollection = item
If subCollection.Count > nCols Then
nCols = subCollection.Count
End If
Next item
Next item
Dim result(nRows, NCols) As Variant
' Now loop through the collections again and fill the result array

The problem that you have is that you cannot change the rank (dimensions) of an array with a redim statement.
dim sent() creates a 1-rank array, redim sent2(x, y) assumes a 2-rank array. Try dim sent(,).
Also, it will improve performance (and code robustness) if you use
dim sent1() as string
dim sent2(,) as string

In case anyone has this problem here is how I solved it:
<code>
Function find_sentences_complex(instring As String) As Variant
Dim xorr As String: xorr = ChrW(&H22BB)
Dim triple_bar As String: triple_bar = ChrW(&H2261)
Dim idisj As String: idisj = ChrW(&H2228)
Dim cond As String: cond = ChrW(&H2192)
Dim x As Integer, y As Integer, z As Integer, b As Integer
Dim total As Integer, paren_closure As Integer, marker As Boolean
Dim n As Integer
Dim sent1() As Variant, sent3() As Variant
'Dim final1d As Integer, final2d As Integer
Dim b_arr() As Integer
Dim b_max As Integer
Dim temp_string As String
For x = InStr(instring, "(") To Len(instring) Step 1
temp_string = Mid(instring, x, 1)
If Mid(instring, x, 1) = "(" Then
If marker = False Then
z = x
marker = True
End If
total = total + 1
ElseIf Mid(instring, x, 1) = ")" Then
total = total - 1
If total = 0 Then
marker = False
b = b + 1
paren_closure = x
ReDim Preserve sent1(b)
sent1(b) = Mid(instring, z, (x - z) + 1)
End If
End If
Next
Dim temp_sent1 As String
total = 0
marker = False
b = 0
Dim sent2()
ReDim sent2(UBound(sent1), 5)
For n = 1 To UBound(sent1)
temp_sent1 = sent1(n)
temp_sent1 = Mid(temp_sent1, 2, Len(temp_sent1) - 2)
b = 0
For x = 1 To Len(temp_sent1)
temp_string = Mid(instring, x, 1)
If Mid(temp_sent1, x, 1) = "(" Then
If marker = False Then
z = x
marker = True
End If
total = total + 1
ElseIf Mid(temp_sent1, x, 1) = ")" Then
total = total - 1
If total = 0 Then
marker = False
b = b + 1
paren_closure = x
'ReDim Preserve sent2(n, b)
sent2(n, b) = Mid(temp_sent1, z, (x - z) + 1)
End If
End If
Next
'this part of the code redimensions the side of the array
ReDim Preserve b_arr(n)
b_arr(n) = b
Next
b_max = MaxValOfIntArray(b_arr)
ReDim Preserve sent2(UBound(sent1), b_max)
End Function
Public Function MaxValOfIntArray(ByRef TheArray As Variant) As Integer
'This function gives max value of int array without sorting an array
Dim i As Integer
Dim MaxIntegersIndex As Integer
MaxIntegersIndex = 0
For i = 1 To UBound(TheArray)
If TheArray(i) > TheArray(MaxIntegersIndex) Then
MaxIntegersIndex = i
End If
Next
'index of max value is MaxValOfIntArray
MaxValOfIntArray = TheArray(MaxIntegersIndex)
End Function
</code>

Related

Select random item from 1d array no repeat

I am trying to select random item from 1d array using this code
Sub Select_Random_Item_From_1D_Array()
Dim arr(), x As Long
arr = Array("Good", "Very Good", "Excellent")
Randomize
x = Int((UBound(arr) + 1) * Rnd + 1)
Debug.Print arr(x - 1)
End Sub
How can I be able to prevent a repetition? I mean I need to select all the items randomly with no repetition. And if all the items are selected then to reset the process. Simply I need to select all the items randomly
This is a simple way to return a random permutation of an array that takes exactly n steps, where n is the number of entries in the array.
Dim arr(), x As Long, r As Long
arr = Array("Good", "Very Good", "Excellent")
x = UBound(arr)
While x >= 0
r = Int(Rnd * x)
Debug.Print arr(r)
arr(r) = arr(x)
x = x - 1
Wend
Pick r at random from (0,..,x) and print out arr(r). Then replace the entry at r with the entry at x, and choose again, but this time from (0,..,x-1), and repeat until x=0.
A fuller version that lets you read one entry at a time is here:
Place this in a module:
Public rarr(), ctr As Integer, arr()
Sub init()
With Cells
.Clear
.ColumnWidth = 10
End With
Dim x As Long, r As Long
arr = Array("Very Poor", "Poor", "Average", "Good", "Very Good", "Excellent")
x = UBound(arr)
ReDim rarr(0 To x)
Randomize
While x >= 0
r = Int(Rnd * x)
rarr(x) = arr(r)
arr(r) = arr(x)
x = x - 1
Wend
[a1:f1] = rarr
ctr = 0
End Sub
Sub Button1_Click()
Cells(ctr + 3, 1) = rarr(ctr)
ctr = ctr + 1
If ctr > UBound(rarr) Then init
End Sub
and add two buttons to the worksheet. Point one at init and the other at Button1_Click. Click init first, and then pressing Button1 displays a random and unique entry one at a time.
You could create a second array of Booleans, having the same length.
This array is initialized with only False. If a value from your array is selected by the rand, then set the boolean array matching value to True. And if the rand value next time is on a value which has already been selected (with a True in the boolean array), do the rand again
Try this little example step by step, you'll see the logic:
Sub Select_Random_Item_From_1D_Array()
Dim arr(), x As Long, cpt As Long
Dim mBool(2) As Boolean
cpt = 0
arr = Array("Good", "Very Good", "Excellent")
Do While cpt < 3 '3 being the number of items in your array + 1 (from 0 to 2)
Randomize
x = Int((UBound(arr) + 1) * Rnd + 1)
If mBool(x - 1) = False Then
mBool(x - 1) = True
Debug.Print arr(x - 1)
cpt = cpt + 1
End If
Loop
End Sub
It will print a random item from your array, and every time it does so it changes the matching value of the 2nd array from False to True. Then it does it again and if it has already been printed (if the matching value on the boolean array is True) it tries again.
I added a variable named cpt, which goes from 0 to the number of items in your array, it makes the algorithm stop when it has printed all the items one time.
This is probably not he best way to do what you want, but it works and it's not that complicated
Select Random Item Series
Option Explicit
Sub resetRandomItem()
getRandomItem
End Sub
Sub selectRandomItem()
Dim arr As Variant
arr = Array("Bad", "Better", "Good", "Very Good", "Excellent")
Debug.Print getRandomItem(arr)
End Sub
' If x elements in 1D array, it returns a series of x different values.
Function getRandomItem(Optional Data1D As Variant) As Variant
Static arr As Variant
' Reset 'arr': use 'getRandomItem' without 'Data1D' parameter.
If IsMissing(Data1D) Then arr = Empty: Exit Function
If IsEmpty(arr) Then arr = Data1D
Dim lb As Long: lb = LBound(arr)
Dim ub As Long: ub = UBound(arr)
If lb = ub Then
getRandomItem = arr(lb)
arr = Empty
Else
Randomize
Dim x As Long: x = Int((ub - lb + 1) * Rnd + 1)
Dim y As Long: y = x + lb - 1
getRandomItem = arr(y)
arr(y) = Empty
Dim i As Long, k As Long
For i = lb To ub - 1
If arr(i) = Empty Then
For k = i + 1 To ub
arr(k - 1) = arr(k)
Next k
Exit For
End If
Next i
ReDim Preserve arr(ub - 1)
End If
End Function

Getting a specific value from excel chart with vba

I am trying to get a specific value from an excel chart. This is the code which creates my chart (I created a reversed binomial distribution plot) :
Dim lim As String
Dim N As Long
N = Range("C4").Value
Dim x, s, p As Double
x = Range("C6") 'event number
s = Range("C5") 'sample size
Dim g() As Long
Dim h() As Double
Dim k() As Double
Dim prob() As Double
ReDim g(N)
ReDim prob(N)
ReDim h(N)
ReDim k(N)
For i = 1 To N
g(i) = i
h(i) = i / N
k(i) = 1 - h(i)
prob(i) = WorksheetFunction.BinomDist(x, s, h(i), False) * 100
End If
And here is chart:
I need the point where y is 0 on distribution curve second time.
At the end of your For Loop, you could check if prob(i) = 0 And Prob(i-1) > 0, and save the index of this point. It's "too" simple, yet if this is just for this kind of distribution, it do the job :
Dim targetIndex As Integer
For i = 1 To N
g(i) = i
h(i) = i / N
k(i) = 1 - h(i)
prob(i) = WorksheetFunction.BinomDist(x, s, h(i), False) * 100
If i > 1 Then 'check if this is not the first point
If prob(i) = 0 And prob(i-1) <> 0 Then targetIndex = i
End If
Next
'// Now your point is the couple (targetIndex, prob(targetIndex))

How to find FFT in Excel without using Fourier Analysis function?

I am trying to write an FFT application in Excel that claculates frequencies, amplitude and phase. I know how to use the in-built function but the data I am trying to analyse has 32,795 points, more than the maximum 4096 for the in-buit function.
Does anyone know how I can either (1) Increase the maximum number of data inputs? (2) Write my own macro to avoid using the in-built function (if this allows me to analyse all the points)? or (3) Start over in Matlab or a with programming language that allows me to analyse all the points and get all the data I need?
You can easily use the matlab built in function and it doesnt have the limitation like Excel and then import the results to excel
Yes, Excel FFT has the limit of data point 4096 and slow.
I programmed FFT using only Excel VBA code and there is no limit of the data point.
Below is the performance for the data point count.
There was a part where I could speed it up a bit, but I didn't b/c it makes the code less readable. However, even now, it may be the fastest FFT code in the Excel.
[data point] [FFT execution time]
4 kB 62ms
16 kB 235ms
64 kB 984ms
Computer Specification: 11th Gen Intel(R) Core(TM) i7-1165G7 # 2.80GHz
I implemented Cooley-Tukey algorithm, and use several techniques to speed-up the code running time in the Excel environment.
You can find the code and download the excel file in here. (https://infograph.tistory.com/351)
Otherwise you can review main logic as below:
'Module: fftProgram
'Author: HJ Park
'Date : 2019.5.18(v1.0), 2022.8.1(v2.0)
Option Explicit
Public Const myPI As Double = 3.14159265358979
Public Function Log2(X As Long) As Double
Log2 = Log(X) / Log(2)
End Function
Public Function Ceiling(ByVal X As Double, Optional ByVal Factor As Double = 1) As Double
' X is the value you want to round
' Factor is the multiple to which you want to round
Ceiling = (Int(X / Factor) - (X / Factor - Int(X / Factor) > 0)) * Factor
End Function
Public Function Floor(ByVal X As Double, Optional ByVal Factor As Double = 1) As Double
' X is the value you want to round
' Factor is the multiple to which you want to round
Floor = Int(X / Factor) * Factor
End Function
' return 0 if N is 2^n value,
' return (2^n - N) if N is not 2^n value. 2^n is Ceiling value.
' return -1, if error
Public Function IsPowerOfTwo(N As Long) As Long
If N = 0 Then GoTo EXIT_FUNCTION
Dim c As Long, F As Double
c = Ceiling(Log2(N)) 'Factor=0, therefore C is an integer number
F = Floor(Log2(N))
If c = F Then
IsPowerOfTwo = 0
Else
IsPowerOfTwo = (2 ^ c - N)
End If
Exit Function
EXIT_FUNCTION:
IsPowerOfTwo = -1
End Function
''''''''''''''''''''''''''''
'''''''''''''''''''''''''''''
Function MakePowerOfTwoSize(ByRef r As Range, ByVal fillCount As Long) As Boolean
Dim arr() As Integer
On Error GoTo ERROR_HANDLE
'1)make a array with zero
ReDim arr(0 To fillCount - 1) As Integer
'2)set a range to be filled with zero
Dim fillRowStart As Long
Dim fillRange As Range
fillRowStart = r.Row + r.Rows.Count
Set fillRange = Range(Cells(fillRowStart, r.Column), Cells(fillRowStart + fillCount - 1, r.Column))
'3)fill as zero
fillRange = arr
'4)update range area to be extended
Set r = Union(r, fillRange)
MakePowerOfTwoSize = True
Exit Function
ERROR_HANDLE:
MakePowerOfTwoSize = False
End Function
' read the range and return it as complex value array
Function Range2Array(r As Range) As Complex()
Dim i As Long, size As Long
Dim arr() As Complex
size = r.Rows.Count
ReDim arr(0 To size - 1) As Complex
Dim re As Double, im As Double
On Error GoTo ERROR_HANDLE
For i = 1 To size
arr(i - 1) = String2Complex(r.Rows(i).Value)
Next i
Range2Array = arr
Exit Function
ERROR_HANDLE:
MsgBox "Error: " & i
End Function
Function ArrangedNum(num As Long, numOfBits As Integer) As Long
Dim arr() As Byte
Dim i As Integer, j As Integer
Dim k As Long
If (2 ^ numOfBits) <= num Then GoTo EXIT_FUNCTION
'1) Decimal number -> Reversed Binary array : (13,4) -> {1,1,0,1} -> {1,0,1,1}
ReDim arr(0 To numOfBits - 1) As Byte
For i = 0 To numOfBits - 1
j = (numOfBits - 1) - i
k = Int((num / (2 ^ j)))
arr(j) = (k And 1)
Next i
'2) Reversed Binary -> Decimal: {1,0,1,1} -> 1*2^3 + 0*2^2 + 1*2&1 + 1 = 11
Dim d As Long
For i = 0 To numOfBits - 1
d = d + (arr(i) * 2 ^ (numOfBits - 1 - i))
Next i
ArrangedNum = d
Exit Function
EXIT_FUNCTION:
ArrangedNum = 0
End Function
' rangeArr[1 to n, 1]
Function arrangeToFFTArray(arr() As Complex, size As Long, numOfBits As Integer) As Complex()
Dim i As Long, j As Long
Dim arrangedArr() As Complex
ReDim arrangedArr(0 To size - 1) As Complex
For i = 0 To size - 1
j = ArrangedNum(i, numOfBits) '{000,001,010, 011, 100, 101, 110, 111} -> {0, 4, 2, 6, 1, 5, 3, 7}
arrangedArr(j) = arr(i)
Next i
arrangeToFFTArray = arrangedArr
End Function
' calculate convolution ring W
' W[k] = cos(theta) - isin(theta)
' theta = (2pi*k/N)
Function CalculateW(cnt As Long, isInverse As Boolean) As Complex()
Dim arr() As Complex
Dim i As Long
Dim T As Double, theta As Double
Dim N As Long, N2 As Long
N = cnt
N2 = N / 2
ReDim arr(0 To N2 - 1) As Complex 'enough to calculate 0 to (N/2 -1)
T = 2 * myPI / CDbl(N)
If isInverse Then
For i = 0 To N2 - 1
theta = -(T * i)
arr(i) = Cplx(Cos(theta), -Sin(theta))
Next i
Else
For i = 0 To N2 - 1
theta = T * i
arr(i) = Cplx(Cos(theta), -Sin(theta))
Next i
End If
CalculateW = arr
End Function
' X({0,1}, [0,n-1]): 2d array. (0, n) <--> (1,n)
' src: src index of the array. 0 or 1
' tgt: tgt index of the array. 1 or 0
' s : starting index of the data in the array
' size: region size to be calculated
' kJump : k's jumping value
' W(0 ~ n-1) : Convolution ring
Sub RegionFFT(X() As Complex, src As Integer, tgt As Integer, _
s As Long, size As Long, kJump As Long, W() As Complex)
Dim i As Long, e As Long
Dim half As Long
Dim k As Long
Dim T As Complex
' Xm+1[i] = Xm[i] + Xm[i+half]W[k]
' Xm+1[i+half] = Xm[i] - Xm[i+half]W[k]
k = 0
e = s + (size / 2) - 1
half = size / 2
For i = s To e
T = CMult(X(src, i + half), W(k))
X(tgt, i) = CAdd(X(src, i), T)
X(tgt, i + half) = CSub(X(src, i), T)
k = k + kJump
Next i
End Sub
Sub WriteToTarget(tgtRange As Range, X() As Complex, tgtIdx As Integer, N As Long, roundDigit As Integer)
Dim i As Long
Dim arr() As Variant
ReDim arr(0 To N - 1) As Variant
For i = 0 To N - 1
If X(tgtIdx, i).im < 0 Then
arr(i) = Round(X(tgtIdx, i).re, roundDigit) & Round(X(tgtIdx, i).im, roundDigit) & "i"
Else
arr(i) = Round(X(tgtIdx, i).re, roundDigit) & "+" & Round(X(tgtIdx, i).im, roundDigit) & "i"
End If
Next i
tgtRange.Rows = Application.Transpose(arr)
End Sub
' xRange: input data
' tgtRange: output range
' isInverse: FFT or IFFT
Public Function FFT_Forward(xRange As Range, tgtRangeStart As Range, roundDigit As Integer, isInverse As Boolean) As Complex()
Dim i As Long, N As Long
Dim totalLoop As Integer, curLoop As Integer 'enough as Integer b/c it is used for loop varoable
Dim xArr() As Complex, xSortedArr() As Complex
Dim W() As Complex 'convolution ring
Dim X() As Complex 'output result
Dim errMsg As String
errMsg = "Uncatched error"
'1) check whether 2^r count data, if not pad to zero
Dim fillCount As Long
N = xRange.Rows.Count
fillCount = IsPowerOfTwo(N)
If fillCount = -1 Then
errMsg = "No input data. Choose input data"
GoTo ERROR_HANDLE
End If
If fillCount <> 0 Then
If MakePowerOfTwoSize(xRange, fillCount) = False Then 'xRange's size will be chnaged
errMsg = "Error while zero padding"
GoTo ERROR_HANDLE
End If
End If
'2) calculate loop count for FFT: 2->1 4->2 8->3 ...
N = xRange.Rows.Count 'xRange's size can be changed so read one more...
totalLoop = Log2(N)
'3) sort x for 2's FFT : convert to reversed binary and then convert to decimal
xArr = Range2Array(xRange) 'xArr[0,n-1]
xSortedArr = arrangeToFFTArray(xArr, N, totalLoop) 'xSortedArr[0,n-1]
'4) calculate W
W = CalculateW(N, isInverse)
'5) use 2-dimensional array to save memory space. X[0, ] <-> X[1, ]
ReDim X(0 To 1, 0 To N - 1) As Complex
For i = 0 To N - 1
X(0, i) = xSortedArr(i)
Next i
'6) Do 2's FFT with sorted x
Dim srcIdx As Integer, tgtIdx As Integer
Dim kJump As Long, regionSize As Long
tgtIdx = 0
For curLoop = 0 To totalLoop - 1
tgtIdx = (tgtIdx + 1) Mod 2
srcIdx = (tgtIdx + 1) Mod 2
regionSize = 2 ^ (curLoop + 1) ' if N=8: 2 -> 4 -> 8
kJump = 2 ^ (totalLoop - curLoop - 1) ' if N=8: 4 -> 2 -> 1
i = 0
Do While i < N
Call RegionFFT(X, srcIdx, tgtIdx, i, regionSize, kJump, W)
i = i + regionSize
Loop
Next curLoop
'7)return the value
Dim resultIdx As Integer
If (totalLoop Mod 2) = 0 Then resultIdx = 0 Else resultIdx = 1
Dim result() As Complex
ReDim result(0 To N - 1) As Complex
If isInverse = True Then
For i = 0 To N - 1
result(i) = CDivR(X(resultIdx, i), N)
Next i
Else
For i = 0 To N - 1
result(i) = X(resultIdx, i)
Next i
End If
FFT_Forward = result
Exit Function
ERROR_HANDLE:
Err.Raise Number:=vbObjectError, Description:=("FFT calculation error: " & errMsg)
End Function
Public Sub FFT(xRange As Range, tgtRangeStart As Range, roundDigit As Integer)
Dim X() As Complex
Dim tgtRange As Range
'1. calculate FFT_forward value
On Error GoTo ERROR_HANDLE
X = FFT_Forward(xRange, tgtRangeStart, roundDigit, False)
'2. write to the worksheet
Dim N As Long
N = UBound(X) - LBound(X) + 1
Dim i As Long
Dim arr() As Variant
ReDim arr(0 To N - 1) As Variant
For i = 0 To N - 1
If X(i).im < 0 Then
arr(i) = Round(X(i).re, roundDigit) & Round(X(i).im, roundDigit) & "i"
Else
arr(i) = Round(X(i).re, roundDigit) & "+" & Round(X(i).im, roundDigit) & "i"
End If
Next i
Set tgtRange = Range(Cells(tgtRangeStart.Row, tgtRangeStart.Column), Cells(tgtRangeStart.Row + N - 1, tgtRangeStart.Column))
tgtRange.Rows = Application.Transpose(arr)
Exit Sub
ERROR_HANDLE:
End Sub
Public Sub IFFT(xRange As Range, tgtRangeStart As Range, roundDigit As Integer)
Dim X() As Complex
Dim tgtRange As Range
'1. calculate FFT_forward value
On Error GoTo ERROR_HANDLE
X = FFT_Forward(xRange, tgtRangeStart, roundDigit, True)
'2.write to the worksheet
Dim N As Long
N = UBound(X) - LBound(X) + 1
Dim arr() As Variant
ReDim arr(0 To N - 1) As Variant
Dim i As Long
For i = 0 To N - 1
arr(i) = Round(X(i).re, roundDigit)
Next i
Set tgtRange = Range(Cells(tgtRangeStart.Row, tgtRangeStart.Column), Cells(tgtRangeStart.Row + N - 1, tgtRangeStart.Column))
tgtRange.Rows = Application.Transpose(arr)
Exit Sub
ERROR_HANDLE:
End Sub
Sub LoadFFTForm()
FFT_Form.Show
In alternative to the VBA solution from HeeJin, with LAMBDA functions in recent versions of Excel it is possible to implement the FFT as a pure formula (i.e. without VBA).
One such implementation is https://github.com/altomani/XL-FFT.
For power of two length it uses a recursive radix-2 Cooley-Tukey algorithm
and for other length a version of Bluestein's algorithm that reduces the calculation to a power of two case.

redimension dynamic multi dimension arrays in Excel VBA [duplicate]

Take a look at the following code. What my problem is is that I can't figure out how to redimension the n integer and the b integer. What I'm doing is the array sent1 is already working and it is populated with about 4 sentences. I need to go through each sentence and work on it but I'm having trouble.
dim sent1()
dim sent2()
dim n as integer, b as integer, x as integer
dim temp_sent as string
b = 0
For n = 1 to ubound(sent1)
temp_sent = sent1(n)
for x = 1 to len(temp_sent1)
code
if a then
b = b + 1
'**THIS IS THE PART OF THE CODE THAT IS NOT WORKING**
redim preserve sent2(1 to ubound(sent1), b)
sent2(n,b) = [code]
next
next
There are two issues in your code:
When you Dim an array without specifying the lower bound it will by default be 0 based (unless you have specified Option Base 1). When explicitly specified, lower bound can be any number, not just 0 or 1
For a multi dimensioned array, Redim Preserve can only change the last dimension, and then only the upper bound.
In general, I find it better to always specify Lower and Upper bounds, eg
Redim MyArray(1 to 10, 0 to 99)
Is there any specific reason why you want to / must use arrays?
If not, I'd suggest using collections instead. You can also have nested collections, e.g.
Dim dimension1 As New Collection
Dim dimension2 AS New Collection
dimension1.Add dimension2
etc.
That way, you won't have to worry about increasing dimensions manually at all. If you need to convert it back to a 2D Array, you can do sth like this in the end
Dim item AS Variant
Dim subCollection AS Collection
Dim nRows AS Integer
Dim nCols AS integer
' assuming "col" is your jagged collection
nRows = col.Count
For Each item in col
If TypeOf item is Collection
Set subCollection = item
If subCollection.Count > nCols Then
nCols = subCollection.Count
End If
Next item
Next item
Dim result(nRows, NCols) As Variant
' Now loop through the collections again and fill the result array
The problem that you have is that you cannot change the rank (dimensions) of an array with a redim statement.
dim sent() creates a 1-rank array, redim sent2(x, y) assumes a 2-rank array. Try dim sent(,).
Also, it will improve performance (and code robustness) if you use
dim sent1() as string
dim sent2(,) as string
In case anyone has this problem here is how I solved it:
<code>
Function find_sentences_complex(instring As String) As Variant
Dim xorr As String: xorr = ChrW(&H22BB)
Dim triple_bar As String: triple_bar = ChrW(&H2261)
Dim idisj As String: idisj = ChrW(&H2228)
Dim cond As String: cond = ChrW(&H2192)
Dim x As Integer, y As Integer, z As Integer, b As Integer
Dim total As Integer, paren_closure As Integer, marker As Boolean
Dim n As Integer
Dim sent1() As Variant, sent3() As Variant
'Dim final1d As Integer, final2d As Integer
Dim b_arr() As Integer
Dim b_max As Integer
Dim temp_string As String
For x = InStr(instring, "(") To Len(instring) Step 1
temp_string = Mid(instring, x, 1)
If Mid(instring, x, 1) = "(" Then
If marker = False Then
z = x
marker = True
End If
total = total + 1
ElseIf Mid(instring, x, 1) = ")" Then
total = total - 1
If total = 0 Then
marker = False
b = b + 1
paren_closure = x
ReDim Preserve sent1(b)
sent1(b) = Mid(instring, z, (x - z) + 1)
End If
End If
Next
Dim temp_sent1 As String
total = 0
marker = False
b = 0
Dim sent2()
ReDim sent2(UBound(sent1), 5)
For n = 1 To UBound(sent1)
temp_sent1 = sent1(n)
temp_sent1 = Mid(temp_sent1, 2, Len(temp_sent1) - 2)
b = 0
For x = 1 To Len(temp_sent1)
temp_string = Mid(instring, x, 1)
If Mid(temp_sent1, x, 1) = "(" Then
If marker = False Then
z = x
marker = True
End If
total = total + 1
ElseIf Mid(temp_sent1, x, 1) = ")" Then
total = total - 1
If total = 0 Then
marker = False
b = b + 1
paren_closure = x
'ReDim Preserve sent2(n, b)
sent2(n, b) = Mid(temp_sent1, z, (x - z) + 1)
End If
End If
Next
'this part of the code redimensions the side of the array
ReDim Preserve b_arr(n)
b_arr(n) = b
Next
b_max = MaxValOfIntArray(b_arr)
ReDim Preserve sent2(UBound(sent1), b_max)
End Function
Public Function MaxValOfIntArray(ByRef TheArray As Variant) As Integer
'This function gives max value of int array without sorting an array
Dim i As Integer
Dim MaxIntegersIndex As Integer
MaxIntegersIndex = 0
For i = 1 To UBound(TheArray)
If TheArray(i) > TheArray(MaxIntegersIndex) Then
MaxIntegersIndex = i
End If
Next
'index of max value is MaxValOfIntArray
MaxValOfIntArray = TheArray(MaxIntegersIndex)
End Function
</code>

Simple Histogram in VBA?

I have data stored in some column (Say, Column A). The length of Column A is not fixed (depends on previous steps in the code).
I need a histogram for the values in Column A, and have it in the same sheet. I need to take the values in column A, and automatically compute M Bins, then give the plot.
I looked online for a "simple" code, but all codes are really fancy, with tons of details that I don't need, to the extent that I am not even able to use it. (I am a VBA beginner.)
I found the following code that seems to do the job, but I am having trouble even calling the function. Besides, it only does computations but does not make the plot.
Sub Hist(M As Long, arr() As Single)
Dim i As Long, j As Long
Dim Length As Single
ReDim breaks(M) As Single
ReDim freq(M) As Single
For i = 1 To M
freq(i) = 0
Next i
Length = (arr(UBound(arr)) - arr(1)) / M
For i = 1 To M
breaks(i) = arr(1) + Length * i
Next i
For i = 1 To UBound(arr)
If (arr(i) <= breaks(1)) Then freq(1) = freq(1) + 1
If (arr(i) >= breaks(M - 1)) Then freq(M) = freq(M) + 1
For j = 2 To M - 1
If (arr(i) > breaks(j - 1) And arr(i) <= breaks(j)) Then freq(j) = freq(j) + 1
Next j
Next i
For i = 1 To M
Cells(i, 1) = breaks(i)
Cells(i, 2) = freq(i)
Next i
End Sub
And then I try to call it simply by:
Sub TestTrial()
Dim arr() As Variant
Dim M As Double
Dim N As Range
arr = Range("A1:A10").Value
M = 10
Hist(M, arr) ' This does not work. Gives me Error (= Expected)
End Sub
A little late but still I want to share my solution. I created a Histogram function which might be used as array formula in the excel spread sheet. Note: you must press
CTRL+SHIFT+ENTER to enter the formula into your workbook. Input is the range of values and the number M of bins for the histogram. The output range must have M rows and two columns. One column for the bin value and one column for the bin frequency.
Option Explicit
Option Base 1
Public Function Histogram(arr As Range, M As Long) As Variant
On Error GoTo ErrHandler
Dim val() As Variant
val = arr.Value
Dim i As Long, j As Integer
Dim Length As Single
ReDim breaks(M) As Single
ReDim freq(M) As Integer
Dim min As Single
min = WorksheetFunction.min(val)
Dim max As Single
max = WorksheetFunction.max(val)
Length = (max - min) / M
For i = 1 To M
breaks(i) = min + Length * i
freq(i) = 0
Next i
For i = 1 To UBound(val)
If IsNumeric(val(i, 1)) And Not IsEmpty(val(i, 1)) Then
If val(i, 1) > breaks(M) Then
freq(M) = freq(M) + 1
Else
j = Int((val(i, 1) - min) / Length) + 1
freq(j) = freq(j) + 1
End If
End If
Next i
Dim res() As Variant
ReDim res(M, 2)
For i = 1 To M
res(i, 1) = breaks(i)
res(i, 2) = freq(i)
Next i
Histogram = res
ErrHandler:
'Debug.Print Err.Description
End Function
Not 100% sure as to the efficacy of that approach but;
Remove the parens as your calling a sub; Hist M, arr
M is declared as double but received by the function as a long; this won't work so declare it in the calling routine as long
You will need to recieve arr() As Variant
Range -> Array produces a 2 dimensional array so the elements are arr(1, 1) .. arr(n, 1)

Resources