receiving "out of range" error, can't figure out why - vba

Let me start by first thanking everyone for the help/intention to help. This community is phenomenal. Second: I'm pretty new at this- before this week I'd learned basic in highschool a decade ago but no other programming experience outside of theory.
Without further ado, here's my issue:
Working on code to find unique variables (I know there's a lot of opensource stuff out there, need to customize this though). When I go to populate the array with the very first string I run into an 'out of range' error at array(1), which I had explicity set (1 TO UB), with UB being the upper bound. I've also double checked the value of UB with msgbox and it's at 15 with my dummy data, so that shouldn't be an issue. I've set the values in the array to empty (have also done so with 0, to no avail).
The error occurs at "ResultArray(1) = CurrentArray(1)"
I'm at a loss; any assistance would be much appreciated.
Option Explicit
Sub unque_values()
'''''''''Variable declaration
'
' CurrentArray() is the array taken from the worksheet
' Comp is the method of comparing inputs (either case sensitive or case insensitive)
' resultarray() is the array that unique values are placed
' UB is the upper bound of Result Array
' resultindex is the variable that keeps track of which cells are unique and which are not
' n is a helped variable that assists with resizing the array
Dim currentarray() As Variant
Dim comp As VbCompareMethod
Dim resultarray() As Variant
Dim UB As Long
Dim resultindex As Long
Dim n As Long
Dim v As Variant
Dim inresults As Boolean
Dim m As Long
' set variables to default values
Let comp = vbTextCompare
Let n = 0
' count the number of cells included in currentarray and populate with values
Let n = ActiveWorkbook.Worksheets("Data").Range("A:A").Count
Let UB = ActiveWorkbook.Worksheets("Data").Range("A" & n).End(xlUp).Row
' dimension arrays
ReDim resultarray(1 To UB)
ReDim currentarray(1 To UB)
' don't forget to change to named ranges
Let currentarray() = Range("f2", "f" & UB)
' populate resultarray with empty values
For n = LBound(resultarray) To UBound(resultarray)
resultarray(n) = Empty
Next n
MsgBox (n)
'check for invalid values in array
For Each v In currentarray
If IsNull(n) = True Then
resultarray = CVErr(xlErrNull)
Exit Sub
End If
Next v
' assumes the first value is unique
resultindex = 1
'''''''''''''''''''''''''''''''''''''''''error is this line''''''''''''''
resultarray(1) = currentarray(1)
' Search for duplicates by cycling through loops
' n = index of value being checked
' m = index of value being checked against
For n = 2 To UB
Let inresults = False
For m = 1 To n
If StrComp(CStr(resultarray(m)), CStr(currentarray(n)), comp) = 0 Then
inresults = True
Exit For
End If
Next m
If inresults = False Then
resultindex = resultindex + 1
resultarray(resultindex) = currentarray(n)
End If
Next n
ReDim Preserve resultarray(1 To resultindex)
End Sub

You've assigned to currentArray a range array. These are always two-dimensional arrays.
You should be able to resolve it with:
resultarray(1) = currentarray(1, 1)
You would need to modify a few more lines in your code to refer to both dimensions of the array.
Alternatively, with the least manipulation to your existing code, transpose the array which turns it to a one-dimensional array. This should require no other changes to your code.
Let currentArray() = Application.Transpose(Range("f2", "f" & UB))

Try with ActiveWorkbook.Worksheets("Data").UsedRange.Columns(1).cells.Count

Related

VBA - check for duplicates while filling cells through a loop

I am writing a VBA code that goes through a defined matrix size and filling cells randomly within its limits.
I got the code here from a user on stackoverflow, but after testing it I realized that it does not fit for avoiding duplicate filling, and for instance when filling 5 cells, I could only see 4 cells filled, meaning that the random filling worked on a previously filled cell.
This is the code I'm working with:
Dim lRandom As Long
Dim sCells As String
Dim sRandom As String
Dim rMolecules As Range
Dim i As Integer, j As Integer
Dim lArea As Long
lArea = 400 '20x20
'Populate string of cells that make up the container so they can be chosen at random
For i = 1 To 20
For j = 1 To 20
sCells = sCells & "|" & Cells(i, j).Address
Next j
Next i
sCells = sCells & "|"
'Color the molecules at random
For i = 1 To WorksheetFunction.Min(5, lArea)
Randomize
lRandom = Int(Rnd() * 400) + 1
sRandom = Split(sCells, "|")(lRandom)
Select Case (i = 1)
Case True: Set rMolecules = Range(sRandom)
Case Else: Set rMolecules = Union(rMolecules, Range(Split(sCells, "|")(lRandom)))
End Select
sCells = Replace(sCells, "|" & sRandom & "|", "|")
lArea = lArea - 1
Next i
rMolecules.Interior.ColorIndex = 5
Using this same exact code which works perfectly, WHAT can I insert and WHERE do I do that so that the code would check if a cell is previously already filled with a string or a color?
I feel as though this code I'm looking for should be right before
rMolecules.Interior.ColorIndex = 5
But I'm not sure what to type.
EDIT
From the comments I realized that I should be more specific.
I am trying to randomly fill cells with the blue color (.ColorIndex = 5), but what I need to check first is if the randomizing hadn't marked a cell twice, so that for instance in this case, if I want to mark 5 different cells, it marks only 4 of them because of a duplicate and thus fills only 4 cells with the blue color. I need to avoid that and make it choose another cell to mark/fill.
I'd appreciate your help.
Keep the cells you use in a Collection and remove them as you fill the random cells:
Sub FillRandomCells(targetRange As Range, numberOfCells As Long)
' populate collection of unique cells
Dim c As Range
Dim targetCells As New Collection
' make sure arguments make sense
If numberOfCells > targetRange.Cells.Count Then
Err.Raise vbObjectError, "FillRandomCells()", _
"Number of cells to be changed can not exceed number of cells in range"
End If
For Each c In targetRange.Cells
targetCells.Add c
Next
' now pick random 5
Dim i As Long, randomIndex As Long
Dim upperbound As Long
Dim lowerbound As Long
For i = 1 To numberOfCells
lowerbound = 1 ' collections start with 1
upperbound = targetCells.Count ' changes as we are removing cells we used
randomIndex = Int((upperbound - lowerbound + 1) * Rnd + lowerbound)
Set c = targetCells(randomIndex)
targetCells.Remove randomIndex ' remove so we don't use it again!
c.Interior.Color = 5 ' do what you need to do here
Next
End Sub
Sub testFillRandomCells()
FillRandomCells ActiveSheet.[a1:t20], 5
FillRandomCells ActiveSheet.[b25:f30], 3
End Sub
EDIT: Changed to make the target range and number of changed cells configurable as arguments to a function. Also added error checking (always do that!).
Why not build a list of random numbers and place in a Scripting.Dictionary, one can use the Dictionary's Exist method to detect duplicates, loop through until you have enough then you can enter your colouring code confident that you have a unique list.

Assigning values to 2-dimensional array

I'm trying to get some data I input with another macro into a 2-dimensional array so I can then apply a function to that data, but no matter what I try I keep getting errors. The data includes strings and numbers. I could always just reference the cells and forget about the array, but that complicates the function. Here's my code:
(Declarations)
Dim nLiens As Byte, nCreditors As Byte
Dim SecurityV As Currency, ASecurityV As Currency
Const adjuster = 0.9
(Relevant subs)
Public Sub VariableDeclaration()
nLiens = InputBox("Enter number of liens in security")
nCreditors = InputBox("Enter number of creditors")
SecurityV = InputBox("Enter security full value")
ASecurityV = adjuster * SecurityV
Call ODebt
End Sub
Sub ODebt()
'
'(...)
'
Dim oDebt() As Variant
ReDim oDebt(1 To nCreditors + 1, 1 To nLiens + 1)
Dim rg As Range
Set rg = Range(Cells(1, 1), Cells(nCreditors + 1, nLiens + 1))
oDebt = rg.Value
MsgBox (oDebt)
'>>> ERROR: type mismatch
Call SAllocation
End Sub
I've tried other alternatives, such as setting the content cell by cell with two 'For' loops and LBound and UBound, but nothing seems to work.
You are getting your error not while filling, but at displaying the array.
It is not possible to just Msgbox an array, since Msgbox expects a String argument. You can, in the other hand, display specific positions (e.g. oDebt(1,1)).
If you want to have a look at all of its contents, either use debug mode and the Local window, or print it to some unused cells.
I would copy the values from the datasheet this way:
Dim oDebt As Variant
Dim rg As Range
Set rg = Range(Cells(1, 1), Cells(nCreditors + 1, nLiens + 1))
oDebt = rg ' get data from sheet
'... do calculations with oDebt array
rg = oDebt ' put data on sheet
In words: you dimension the array automatically by assigning the range. If you need the numeric boundaries, use
nrows = UBound(oDebt, 1)
ncols = UBound(oDebt, 2)
Here you see the meaning of the dimension as well, index 1 is rows, index 2 is columns.

Making Selected Range into an Array and doing stuff with it

I'm trying to teach myself VBA writing some little things. I'm trying to make something that allows you to select some data and then calculates the mean and variance. My code is as follows :
Sub VarianceCalculator()
Dim k As Integer
Dim SelectedData As Range
Dim SelectedDataArray() As Variant
Dim Var As Double
Dim Mu As Double
On Error Resume Next
Set SelectedData = Application.InputBox("Select a range of data to be
calculated", Default:=Selection.Address, Type:=8)
On Error GoTo 0
SelectedDataArray = Range(SelectedData.Address)
k = UBound(SelectedDataArray)
Call VarianceCalculatorWithArray(SelectedDataArray, k)
MsgBox ("The selected data has variance " & Var & " and has mean " & Mu)
End Sub
Sub VarianceCalculatorWithArray(Data() As Variant, k As Integer)
Dim Var As Double
Dim Mu As Double
Dim j As Integer
Dim i As Integer
ReDim Data(k) As Variant
Mu = 0
Var = 0
For j = 0 To k
Mu = Mu + (Data(j)) / (k + 1)
Next j
For i = 0 To k
Var = Var + ((Data(i) - Mu) ^ (2)) / (k + 1)
Next i
End Sub
I think the error is that somehow the data is not getting transferred into the array but I can't find a solution to this.
Thanks!!
There are two major problems:
(1) If you want to pass the variables Var and Mu from one procedure to another you'll have to declare them as public variables before the first procedure. The alternative is to setup VarianceCalculatorWithArray as a function.
(2) The array Data() is 2D as a range consists of rows and columns. So, if you want to use an element from this array, you'll have to address it as Data(1, 1). Also note, that this range array starts with row 1 and column 1. Therefore your for...next statements should start with 1 and not with 0.
Note, that you can always set a breakpoint in order to check if data is transferred into the array.

Type Mismatch error when summing along one dimension in multi dimension array

I have a 2D array and I am trying to add along one dimension. The 2D Array is of type variant and might have some of the elements as null ("")
Here is the code so far
'newArray is 2D Array
Function SumColumn(newArray As Variant, index As Integer) As Double
Dim tempArray() As Double
ReDim tempArray(1 To UBound(newArray))
For i = 1 To (UBound(newArray))
tempArray(i) = CDbl(newArray(i, index))
Next
SumColumn = Application.WorksheetFunction.Sum(tempArray)
End Function
I get a type mismatch error when I am running the above code. Please help me debug
You are probabaly getting a Type mismatch because CDbl(newArray(i, index)) might actually not be a number.
This works for me. Please amend the code to suit your needs.
For demonstration purpose, I am storing an Excel range into a 2D array and then converting it to a 1D temp array. Once that is done, I am simply storing the relevant Numbers in the Double Array and finally calculating the sum.
Lets say that the worksheet looks like this
Option Explicit
Sub Sample()
Dim MyAr, TempAr()
Dim dAr() As Double
Dim n As Long, i As Long
MyAr = ActiveSheet.Range("A1:A10").Value
TempAr = Application.Transpose(MyAr)
ReDim dAr(0 To 0)
n = 0
For i = LBound(TempAr) To UBound(TempAr)
If Len(Trim(TempAr(i))) <> 0 Then
If IsNumeric(Trim(TempAr(i))) Then
ReDim Preserve dAr(0 To n)
dAr(UBound(dAr)) = Trim(TempAr(i))
n = n + 1
End If
End If
Next i
Debug.Print Application.WorksheetFunction.Sum(dAr)
End Sub
And this is the output

VBA UBound returns a negative value

I would like to know what I'm doing wrong...
I have a word document open (in word 2010) with three tables in it. I wanted to test basic table extraction in VBA and followed the instructions http://msdn.microsoft.com/en-us/library/office/aa537149(v=office.11).aspx.
Sub ExtractTableData()
Dim doc As Word.Document
Dim tbl As Word.Table
Dim rng As Word.Range
Dim sData As String
Dim aData1() As String
Dim aData2() As String
Dim aDataAll() As String
Dim nrRecs As Long
Dim nrFields As Long
Dim lRecs As Long
Dim lFields As Long
Set doc = ActiveDocument
Set tbl = doc.Tables(1)
Set rng = tbl.ConvertToText(Separator:=vbTab, _
NestedTables:=False)
' Pick up the delimited text into and put it into a string variable.
sData = rng.Text
' Restore the original table.
doc.Undo
' Strip off last paragraph mark.
sData = Mid(sData, 1, Len(sData) - 1)
' Break up each table row into an array element.
aData1() = Split(sData, vbCr)
nrRecs = UBound(aData1())
' The messagebox below is for debugging purposes and tells you
' how many rows are in the table. It is commented out but can
' be used simply by uncommenting it.
'MsgBox "The table contained " & nrRecs + 1 & " rows"
'Process each row to break down the field information
'into another array.
For lRecs = LBound(aData1()) To nrRecs
aData2() = Split(aData1(lRecs), vbTab)
' We need to do this only once!
If lRecs = LBound(aData1()) Then
nrFields = UBound(aData2())
ReDim Preserve aDataAll(nrRecs, nrFields)
End If
' Now bring the row and field information together
' in a single, two-dimensional array.
For lFields = LBound(aData2()) To nrFields
aDataAll(lRecs, lFields) = aData2(j)
Next
Next
End Sub
I'm getting an error at this line: ReDim Preserve aDataAll(nrRecs, nrFields), which is due to "nrFields" being set to a negative value (-1)...
No idea how the upper bound of the array is a negative value... Any help on this would be much appreciated.
I figured it out - I was trying to extract a nested table. I had to cycle through all sub-tables and extract individually. Also, I had to search for and remove ^p before extraction to retain table structure.
After I had figured it out, I noticed that the MS code sample had an error: aData2(j) should actually be aData2(lFields).
Hope this helps some other newbie!
If UBound is -1 and LBound = 0, the array is empty. You can generate an empty array as follows:
Dim EmptyArray() As String
Dim s As String
EmptyArray = Split("")
Debug.Print (UBound(EmptyArray)) ' displays -1
Debug.Print (LBound(EmptyArray)) ' displays 0
In your case I suspect you need to skip the processing if the array is empty:
aData1 = Split(...)
If (UBound(aData1) < LBound(aData1) Then
' UBound is -1 and LBound is 0, array is empty, nothing to do
Else
' Array is non-empty, do your stuff
End If
Although quite bizarre, it is possible for VARIANT SAFEARRAY to have negative lower and upper bound values for any of the dimensions. The array extent is LBound(,dimension) to UBound(,dimension).
What must be true is UBound >= LBound.
To get the array size, use UBound - LBound + 1.
It used to be convention to set the lower bound using an Option Base statement at the top of VBA code although, of course, that didn't affect arrays being returned by 3rd party libraries. Most folk used to use 1 as the lower bound.