I'm trying implement the next code and get the error -
cant assign to array
Where is the error ? Note that if i type Dim arrf() As Variant instead of Dim arrf(5) As Variant I get error -
type mismatch
Public Function calc(ByVal value As Integer, ByVal num As Integer) As Variant()
Dim arr(5) As Variant
Dim x As Double
If value >= num Then
x = value - Application.RoundDown(value / num, 0) * num
arr(0) = x
arr(1) = num - arr(0)
arr(2) = Application.RoundUp(value / num, 0)
arr(3) = 1
arr(4) = Application.RoundDown(value / num, 0)
arr(5) = 1
Else
x = num - Application.RoundDown(num / value, 0) * value
arr(0) = x
arr(1) = value - arr(0)
arr(2) = Application.RoundUp(num / value, 0)
arr(3) = 1
arr(4) = Application.RoundDown(num / value, 0)
arr(5) = 1
calc = arr
End If
End Function
Sub cellsfunc()
With Application
.DisplayAlerts = False
.EnableEvents = False
.ScreenUpdating = False
End With
Dim lastrow As Integer
Dim counter As Integer
Dim arrf(5) As Variant
lastrow = Cells(Rows.Count, 2).End(xlUp).Row
For counter = 2 To lastrow Step 2
arrf = calc(Cells(4, counter), Cells(4, counter + 1))
Next counter
With Application
.DisplayAlerts = True
.EnableEvents = True
.ScreenUpdating = True
End With
End Sub
thanks ahead to all helpers
You have arrf declared as a fixed size array:
Dim arrf(5) As Variant
An array returning function can't return a fixed size array - only a dynamic one. You just need to declare it as a dynamic array:
Dim arrf() As Variant
There's an issue in your function calc() : it only returns a value when it goes through the else, and not the if
It should probably be this:
Public Function calc(ByVal value As Integer, ByVal num As Integer) As Variant()
Dim arr(5) As Variant
Dim x As Double
If value >= num Then
x = value - Application.RoundDown(value / num, 0) * num
arr(0) = x
arr(1) = num - arr(0)
arr(2) = Application.RoundUp(value / num, 0)
arr(3) = 1
arr(4) = Application.RoundDown(value / num, 0)
arr(5) = 1
Else
x = num - Application.RoundDown(num / value, 0) * value
arr(0) = x
arr(1) = value - arr(0)
arr(2) = Application.RoundUp(num / value, 0)
arr(3) = 1
arr(4) = Application.RoundDown(num / value, 0)
arr(5) = 1
End If
calc = arr ' <------- THIS
End Function
If you use a typed array in VBA script always use ReDim.. size initialization. You may use a typed array in a dictionary value or everywhere like a regular variable.
Public Function readData(ws As Worksheet, arr As Scripting.Dictionary) As Boolean
Dim iRow as long
Dim key as String
Dim sVal() As String
ReDim sVal(0 to 1) as String
For iRow=2 to 1000
key = ws.cells(iRow,1)
sVal(0) = ws.Cells(iRow, 5)
sVal(1) = ws.Cells(iRow, 6)
call arr.Add(key, sVal)
Next
readData=true
End Function
Public Function writeData(ws As Worksheet, arr As Scripting.Dictionary) As Long
Dim iRow as long
Dim key as String
Dim sVal() As String
ReDim sVal(0 to 1) as String
For iRow=2 to 1000
key = ws.cells(iRow,1)
If arr.Exists(key) then
sVal = arr.Item(key)
ws.Cells(iRow, 5) = sVal(0)
ws.Cells(iRow, 6) = sVal(1)
End If
Next
writeData=true
End Function
You need to declare aarf as a regular variant not an array. The VBA will convert it for you.
Dim arrf As Variant
Related
Im looking for some help with a VBA problem I'm having. Basically, I'm collecting information from a source file on sheet1 into static arrays. From those static arrays I'm creating a dynamic array with account numbers, and a calculated value. What I'm trying to do next is create a second dynamic array with only unique account numbers and summing the calculated values in the previous dynamic array. But I have no idea how to do that...
The following is what I have so far.
Dim ClosingCash() As Variant, MarginExcess() As Variant, VarMarg() As Variant, Acct() As Variant, FX() As Variant, UniqueAcct() As Variant, Answers() As Variant
Dim Dim1 As Long, Counter As Long, W_Sum As Long
Sheet1.Activate
Acct = Range("b2", Range("b2").End(xlDown))
ClosingCash = Range("f2", Range("f2").End(xlDown))
MarginExcess = Range("j2", Range("J2").End(xlDown))
FX = Range("n2", Range("n2").End(xlDown))
VarMarg = Range("o2", Range("o2").End(xlDown))
Dim1 = UBound(ClosingCash, 1)
ReDim Answers(1 To Dim1, 1 To 2)
For Counter = 1 To Dim1
Answers(Counter, 1) = Acct(Counter, 1)
Answers(Counter, 2) = (WorksheetFunction.Min(ClosingCash(Counter, 1) + VarMarg(Counter, 1), MarginExcess(Counter, 1)) * FX(Counter, 1))
Next Counter
Sheet3.Activate
Range("a2", Range("a2").Offset(Dim1 - 1, 1)).Value = Answers
What I would like to print out are the unique account numbers, and the sum of Answers(counter, 2) that correspond to that account number, similar to a SumIf.
Any advise would be greatly appreciated!
Sum Unique
In your code you could use it like this:
Dim Data As Variant: Data = getUniqueSum(Answers)
If Not IsEmpty(Data) Then
Sheet3.Range("E2").Resize(UBound(Data, 1), UBound(Data, 2)).Value = Data
End If
The Code
Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Writes the unique values from the first column of a 2D array
' and the sum of the corresponding values in its second column,
' to a 2D one-based two-columns array.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function getUniqueSum( _
Data As Variant) _
As Variant
If IsEmpty(Data) Then Exit Function
With CreateObject("Scripting.Dictionary")
.CompareMode = vbTextCompare
Dim Key As Variant
Dim i As Long
Dim c1 As Long: c1 = LBound(Data, 2)
Dim c2 As Long: c2 = c1 + 1
For i = LBound(Data, 1) To UBound(Data, 1)
Key = Data(i, c1)
If Not IsError(Key) Then
If Len(Key) > 0 Then
.Item(Key) = .Item(Key) + Data(i, c2)
End If
End If
Next i
If .Count = 0 Then Exit Function
Dim Result As Variant: ReDim Result(1 To .Count, 1 To 2)
i = 0
For Each Key In .Keys
i = i + 1
Result(i, 1) = Key
Result(i, 2) = .Item(Key)
Next Key
getUniqueSum = Result
End With
End Function
Try This
Sub GetUniqueSum()
Dim Rng As Range
Dim numRows As Long, endRow As Long, outputRow As Long, i As Long
Dim rangeText As String
Dim acct As Variant
Dim Sum As Double, ClosingCash As Double, MarginExcess As Double
Dim FX As Double, VarMarg As Double
Dim Value As Double, Value2 As Double
'Get the last row as a string
numRows = Range("B2", Range("b2").End(xlDown)).Rows.Count
endRow = CStr(numRows + 1)
rangeText = "B2:O" & endRow
'Sort the range
Set Rng = Range("Sheet2!" & rangeText)
Rng.Sort (Rng.Columns(1))
'Initialize variables
acct = Rng.Cells(2, 1)
outputRow = 1
Sum = 0
'Calculate Sums
For i = 1 To Rng.Rows.Count
If Rng.Cells(i, 1) <> acct Then
'No longer same acct, print out results
outputRow = outputRow + 1
Worksheets("Sheet3").Cells(outputRow, 1) = acct
Worksheets("Sheet3").Cells(outputRow, 2) = Sum
acct = Rng.Cells(i, 1)
Sum = 0
End If
ClosingCash = Rng(i, 5).Value
MarginExcess = Rng(i, 9).Value
FX = Rng(i, 13).Value
VarMarg = Rng(i, 14).Value
Value = ClosingCash + VarMarg
Value2 = MarginExcess * FX
If Value > Value2 Then Value = Value2
Sum = Sum + Value
Next
'Print out last result
Worksheets("Sheet3").Cells(outputRow + 1, 1) = acct
Worksheets("Sheet3").Cells(outputRow + 1, 2) = Sum
End Sub
I am trying to create an array, store values in the array and then write the values of the array to a spreadsheet in VBA. This codes takes 1+ hour to run on my computer and I think that an array could really speed up the code.
However, I need help with creating the array, populating the array from the comboboxes and finally write the values of the array to the worksheet.
Create an n-dimensional array
Fill the n-dimensional array with the values of the ComboBoxes.
Iterate through all ComboBoxes.
Store values in the array
Write values from the array to the spreadsheet
Sub WantToUseArray()
Dim k As Integer
Dim l As Integer
Sheets("Test").ComboBox1.ListIndex = 0
For l = 0 To 25
Sheets("Test").ComboBox3.ListIndex = l
Sheets("Test").ComboBox2.ListIndex = 0
For n = 0 To 25
Sheets("Test").ComboBox4.ListIndex = n
Sheets("Points").Select
Dim LR As Long
LR = Cells(Rows.Count, 1).End(xlUp).Row + 1
Cells(LR, "A").Value = Sheets("Test").Range("G5").Value
Cells(LR, "B").Value = Sheets("Test").Range("G6").Value
Cells(LR, "C").Value = Sheets("Test").Range("O5").Value
Cells(LR, "D").Value = Sheets("Test").Range("O6").Value
Cells(LR, "E").Value = Sheets("Test").Range("X5").Value
Cells(LR, "F").Value = Sheets("Test").Range("X6").Value
Cells(LR, "G").Value = Sheets("Test").Range("G6").Value + Sheets("Test").Range("X6").Value
Cells(LR, "H").Value = Sheets("Test").Range("X6").Value + Sheets("Test").Range("G6").Value
Cells(LR, "I").Value = Sheets("Test").Range("K40").Value
Cells(LR, "J").Value = Sheets("Test").Range("K41").Value
Cells(LR, "K").Value = Sheets("Test").Range("K51").Value
Cells(LR, "L").Value = Sheets("Test").Range("K52").Value
Next
Next
End Sub
This code goes through each combobox in a given worksheet, generates an array that contains the list values for each comobox list, then prints all of the contents into that first column. myArray only has a single dimension. Its contents are other arrays. If the comoboxes have different list lengths, a jagged array is created.
To help visualize the arrays, enable the Locals Window by going to view in the menu bar and then selecting Locals Window. See pic below the code.
Option Explicit
Sub main()
Dim ws As Worksheet
Dim mainArray() As Variant
Dim ctrl As Object
Dim numComboBoxes As Long
Set ws = ActiveSheet
numComboBoxes = GetNumberOfComboBoxesInSheet(ws)
mainArray = GenerateJaggedArrayComboBoxListValues(ws, numComboBoxes)
PrintArray ws, mainArray
End Sub
Function GetNumberOfComboBoxesInSheet(ByRef ws As Worksheet) As Long
Dim ctrl As Object
For Each ctrl In ws.OLEObjects
If TypeName(ctrl.Object) = "ComboBox" Then
GetNumberOfComboBoxesInSheet = GetNumberOfComboBoxesInSheet + 1
End If
Next ctrl
End Function
Function GenerateJaggedArrayComboBoxListValues(ByRef ws As Worksheet, ByVal numComboBoxes As Long) As Variant()
Dim ctrl As Object
Dim tempPrimaryArray() As Variant
Dim tempArray() As Variant
Dim x As Long
Dim y As Long
Dim listNum As Long
ReDim tempPrimaryArray(0 To numComboBoxes - 1)
x = 0
For Each ctrl In ws.OLEObjects
If TypeName(ctrl.Object) = "ComboBox" Then
y = 0
For listNum = 0 To ctrl.Object.ListCount - 1
ReDim Preserve tempArray(0, 0 To y)
tempArray(0, y) = ctrl.Object.List(listNum, 0)
y = y + 1
Next listNum
tempPrimaryArray(x) = tempArray
Erase tempArray
x = x + 1
End If
Next ctrl
GenerateJaggedArrayComboBoxListValues = tempPrimaryArray()
End Function
Sub PrintArray(ByRef ws As Worksheet, ByRef mainArray As Variant)
Dim counter As Long
Dim x As Long
Dim y As Long
Dim tempArray() As Variant
counter = 1
For x = LBound(mainArray, 1) To UBound(mainArray, 1)
tempArray = mainArray(x)
For y = LBound(tempArray, 2) To UBound(tempArray, 2)
ws.Range("A" & counter) = tempArray(0, y)
counter = counter + 1
Next y
Next x
End Sub
I am using Bloomberg sample code to collect data from Bloomberg through VBA (2d array?) and I have some old vba code that I believe takes a normal 3d array (maybe someone can clarify that for me). The problem is that Bloomberg output an array of elements.
See Bloomberg code below. Then below that is what I want to essentially convert the Bloomberg output into something that the next bit of code will accept.
Private Sub session_ProcessEvent(ByVal obj As Object)
On Error GoTo errHandler
Dim eventObj As blpapicomLib2.Event
Set eventObj = obj
If Application.Ready Then
If eventObj.EventType = PARTIAL_RESPONSE Or eventObj.EventType = RESPONSE Then
Dim it As blpapicomLib2.MessageIterator
Set it = eventObj.CreateMessageIterator()
Do While it.Next()
Dim msg As Message
Set msg = it.Message
Dim securityData As Element
Dim securityName As Element
Dim fieldData As Element
Set securityData = msg.GetElement("securityData")
Set securityName = securityData.GetElement("security")
Set fieldData = securityData.GetElement("fieldData")
Sheet1.Cells(currentRow, 4).Value = securityName.Value
Dim b As Integer
For b = 0 To fieldData.NumValues - 1
Dim fields As blpapicomLib2.Element
Set fields = fieldData.GetValue(b)
Dim a As Integer
Dim numFields As Integer
numFields = fields.NumElements
For a = 0 To numFields - 1
Dim field As Element
Set field = fields.GetElement(a)
Sheet1.Cells(currentRow, a + 5).Value = field.Name & " = " & field.Value
Next
currentRow = currentRow + 1
Next b
Loop
' skip a row for next security
currentRow = currentRow + 1
End If
End If
Exit Sub
errHandler:
MsgBox Err.Description
End Sub
This is the next bit of code I want the Bloomberg output to feed into.
Option Explicit
Dim Count() As Variant
Dim AdjCount() As Variant
Dim Rev() As Variant
Dim Conf() As Variant
Dim ncount() As Integer
Sub CreateSetupsBUY(series As Variant)
Dim x As Integer
Dim Y As Integer
Dim temp1 As Variant
Dim temp2 As Variant
Dim temp3 As Variant
Dim temp4 As Integer
Dim temp5 As Variant
ReDim Count(UBound(series))
ReDim AdjCount(UBound(series))
ReDim Rev(UBound(series))
ReDim Confn(UBound(series))
ReDim ncount(UBound(series))
For x = LBound(series) To UBound(series)
ReDim temp1(UBound(series(x)))
ReDim temp2(UBound(series(x)))
ReDim temp3(UBound(series(x)))
temp4 = 0
ReDim temp5(UBound(series(x)))
For Y = LBound(series(x)) + 5 To UBound(series(x))
If IsNumeric(series(x)(Y, 1)) Then
If series(x)(Y, 4) < series(x)(Y - 4, 4) Then
temp1(Y) = 1 + temp1(Y - 1)
Else
temp1(Y) = 0
End If
If series(x)(Y, 4) > series(x)(Y - 4, 4) Then
temp5(Y) = 1 + temp5(Y - 1)
Else
temp5(Y) = 0
End If
If temp1(Y) > 9 Then
temp2(Y) = 0
Else
temp2(Y) = temp1(Y)
End If
If temp1(Y) = 9 Then
temp4 = temp4 + 1
End If
If series(x)(Y - 1, 4) >= series(x)(Y - 5, 4) Then
temp3(Y) = 1
Else
temp3(Y) = 0
End If
Else
temp1(Y) = 0
temp2(Y) = 0
temp3(Y) = 0
temp4 = 0
temp5(Y) = 0
End If
Next Y
Count(x) = temp1
AdjCount(x) = temp2
Conf(x) = temp3
ncount(x) = temp4
Rev(x) = temp5
Next x
Call CreateCount(series, Count, Conf, ncount, Rev)
End Sub
When I tried connecting the two I get a type error. I assume its because of the way the Bloomberg array is created and unpacked.
Possible solution I have yet to try is to unpack the Bloomberg array and some how build a basic column row array while the Bloomberg array is unpacking.
I am trying to make a function that takes in a 1D array, filters out by empty cells, and then condenses the array and returns it.
Example: [1][2][3][""][4] returns [1][2][3][4]
I keep getting #Value! when I try to call this new array via index().
Function BlankRemover(ArrayToCondense As Variant) As Variant
Dim ArrayWithoutBlanks() As Variant
Dim CellsInArray As Long
Dim ArrayWithoutBlanksIndex As Long
ArrayWithoutBlanksIndex = 1
For CellsInArray = LBound(ArrayToCondense) To UBound(ArrayToCondense)
If ArrayToCondense(CellsInArray) <> "" Then
ArrayWithoutBlanks(ArrayWithoutBlanksIndex) = ArrayToCondense(CellsInArray).Value
ArrayWithoutBlanksIndex = ArrayWithoutBlanksIndex + 1
End If
Next CellsInArray
ReDim Preserve ArrayWithoutBlanks(LBound(ArrayToCondense) To ArrayWithoutBlanksIndex)
ArrayWithoutBlanks = Application.Transpose(ArrayWithoutBlanks)
BlankRemover = ArrayWithoutBlanks
End Function
Try this:
Function BlankRemover(ArrayToCondense As Variant) As Variant()
Dim ArrayWithoutBlanks() As Variant
Dim CellsInArray As Variant
ReDim ArrayWithoutBlanks(1 To 1) As Variant
For Each CellsInArray In ArrayToCondense
If CellsInArray <> "" Then
ArrayWithoutBlanks(UBound(ArrayWithoutBlanks)) = CellsInArray
ReDim Preserve ArrayWithoutBlanks(1 To UBound(ArrayWithoutBlanks) + 1)
End If
Next CellsInArray
ArrayWithoutBlanks = Application.Transpose(ArrayWithoutBlanks)
BlankRemover = Application.Transpose(ArrayWithoutBlanks)
End Function
Try below:
Notes:
You should define BlankRemover as an array: Variant()
.Value not needed at end of ArrayToCondense(CellsInArray)
The code:
Function BlankRemover(ArrayToCondense As Variant) As Variant()
Dim ArrayWithoutBlanks() As Variant
Dim CellsInArray As Long
Dim ArrayWithoutBlanksIndex As Long
ArrayWithoutBlanksIndex = 0
For CellsInArray = LBound(ArrayToCondense) To UBound(ArrayToCondense)
If ArrayToCondense(CellsInArray) <> "" Then
ReDim Preserve ArrayWithoutBlanks(ArrayWithoutBlanksIndex)
ArrayWithoutBlanks(ArrayWithoutBlanksIndex) = ArrayToCondense(CellsInArray)
ArrayWithoutBlanksIndex = ArrayWithoutBlanksIndex + 1
End If
Next CellsInArray
'ArrayWithoutBlanks = Application.Transpose(ArrayWithoutBlanks)
BlankRemover = ArrayWithoutBlanks
End Function 'BlankRemover
You declared the function
Function BlankRemover(ArrayToCondense As Variant) As Variant
so that ArrayToCondense is not an array, to make it an array you switch ArrayToCondense with ArrayToCondense() so the final code will be:
Function BlankRemover(ArrayToCondense As Variant) As Variant()
There are a couple of issues with your code itself. Make the new array initially equal to the size of the original array; then do one "ReDim Preserve" at the end. Also, don't use a value like "1", arrays can have multiple starting indices. Here's what the code would ideally look like for doing this with arrays (though as I'll note below, I don't think that's actually what you want):
Function blankRemover(arr As Variant) As Variant
If Not IsArray(arr) Then
Exit Function
End If
ReDim newArr(LBound(arr) To UBound(arr))
Dim i As Long
Dim j As Long
j = LBound(arr)
For i = LBound(arr) To UBound(arr)
If Not arr(i) = "" Then
newArr(j) = arr(i)
j = j + 1
End If
Next
ReDim Preserve newArr(LBound(arr) To j - 1)
blankRemover = newArr
End Function
But based on your comments, it sounds like you're not actually passing this function an array: you're passing it a range. So you'd actually want to use something like this:
Function blankRemoverRng(rng As Range) As Variant
If Not ((rng.Rows.Count = 1) Xor (rng.Columns.Count = 1)) Then
Exit Function
End If
Dim arr As Variant
arr = narrow2dArray(rng.Value)
ReDim newArr(LBound(arr) To UBound(arr))
Dim i As Long
Dim j As Long
j = LBound(arr)
For i = LBound(arr) To UBound(arr)
If Not arr(i) = "" Then
newArr(j) = arr(i)
j = j + 1
End If
Next
ReDim Preserve newArr(LBound(arr) To j - 1)
blankRemoverRng = newArr
End Function
Function narrow2dArray(ByRef arr As Variant, Optional ByVal newBase As Long = 1) As Variant
'Takes a 2d array which has one dimension of size 1 and converts it to a 1d array with base newBase
'IE it takes an array with these dimensions:
'Dim arr(1 To 10, 1 To 1)
'And turns it into an array with these dimensions:
'Dim arr(1 To 10)
On Error GoTo exitStatement
Dim bigDim As Integer
If Not IsArray(arr) Then
Dim smallArr(1 To 1) As Variant
smallArr(1) = arr
narrow2dArray = smallArr
Exit Function
ElseIf LBound(arr, 1) = UBound(arr, 1) Then
bigDim = 2
ElseIf LBound(arr, 2) = UBound(arr, 2) Then
bigDim = 1
Else
GoTo exitStatement
End If
ReDim tempArr(newBase To UBound(arr, bigDim) - LBound(arr, bigDim) + newBase) As Variant
Dim i As Long
Dim j As Long
j = LBound(arr, bigDim)
If bigDim = 2 Then
For i = LBound(tempArr) To UBound(tempArr)
If IsObject(arr(1, j)) Then
Set tempArr(i) = arr(1, j)
Else
tempArr(i) = arr(1, j)
End If
j = j + 1
Next
Else
For i = LBound(tempArr) To UBound(tempArr)
If IsObject(arr(j, 1)) Then
Set tempArr(i) = arr(j, 1)
Else
tempArr(i) = arr(j, 1)
End If
j = j + 1
Next
End If
On Error GoTo 0
narrow2dArray = tempArr
Exit Function
exitStatement:
MsgBox "Error: One of array's dimensions must have size = 1"
On Error GoTo 0
Stop
End Function
For those who are coming later looking for a simple answer:
Filter(arrayElement, "", False)
Following is my function :
Function FindMin(MinArray() As Integer) As Integer()
Dim Min As Integer
Dim Index As Integer
Dim ReturnArray(2) As Integer
Dim i As Integer
Min = MinArray(0)
Index = 1
'MsgBox UBound(MinArray, 1) - 1
For i = 1 To UBound(MinArray, 1) - 1
If MinArray(i) < Min Then
Min = MinArray(i)
Index = i + 1
End If
Next i
ReturnArray(0) = Min
ReturnArray(1) = Index
'MsgBox ReturnArray(0)
FindMin = ReturnArray()
End Function
Here's the code that assigns it to a Variant
Dim IndexMin As Variant
IndexMin = FindMin(MinArray)
Min = IndexMin(0)
Index = IndexMin(1)
Values are assigned while debugging the code but it get an "Object variable or With block Variable not set Runtime Error.
Any suggestions
The code works if MinArray has been initialized:
Sub MAIN()
Dim IndexMin As Variant
Dim MinArray(0 To 3) As Integer
MinArray(0) = 4
MinArray(1) = 1
MinArray(2) = 12
MinArray(3) = 15
IndexMin = FindMin(MinArray)
Min = IndexMin(0)
Index = IndexMin(1)
MsgBox Min & vbCrLf & Index
End Sub
Function FindMin(MinArray() As Integer) As Integer()
Dim Min As Integer
Dim Index As Integer
Dim ReturnArray(2) As Integer
Dim i As Integer
Min = MinArray(0)
Index = 1
For i = 1 To UBound(MinArray, 1) - 1
If MinArray(i) < Min Then
Min = MinArray(i)
Index = i + 1
End If
Next i
ReturnArray(0) = Min
ReturnArray(1) = Index
FindMin = ReturnArray()
End Function