I want to do a sumif from an array but i am not sure how to reference a full column in an array. For instance i have the following data in excel (in columns A and B) and code that works fine,
RR TT
1 J
2 K
3 J
4 K
5 J
5 K
6 J
7 K
8 J
9 K
Sub test()
Dim s As Range
Dim s2 As Range
Set s = Range("A2:A11")
Set s2 = Range("B2:B11")
e = WorksheetFunction.SumIfs(s, s2, "J")
MsgBox e
End Sub
This sums the RR column where the TT column equals "J" and the answer is 23. But in the code i assign each column to a seperate Range in VBA. I would like to assign both columns to an array and do the sumifs from the array. The first part of the code would then look as follows,
Dim s() As Variant
ReDim s(1 To 10, 1 To 2)
s = Range("A2:B11")
How do i then reference the columns of the array in the sumifs function? (the first two entries in the sumifs function)
e = WorksheetFunction.SumIfs(?, ?, "J")
I will at the end work with a much bigger dataset and if it is possible i would like not create a ton of seperate Ranges but just one array.
You could create a custom function to do this:
Public Function SumIf(lookupTable() As Variant, lookupValue As String) As Long
Dim I As Long
SumIf = 0
For I = LBound(lookupTable) To UBound(lookupTable)
If lookupTable(I, 1) = lookupValue Then
SumIf = SumIf + lookupTable(I, 2)
End If
Next I
End Function
Sub M_snb()
msgbox [sum((A1:A9)*(B1:B9="J"))]
End Sub
or
Sub M_snb()
msgbox [sumproduct((A1:A9)*(B1:B9="J"))]
end sub
or
Sub M_snb()
msgbox [sum(if(B1:B9="J",A1:A9,0))]
end sub
Thanks for the comments and the answers. I don't have to use arrays but it was my first choice and keeping with ranges is fine. I did find got it right with Ranges. And i did the following
Sub test()
Dim s As Range
Set s = Range("A2:B11")
e = WorksheetFunction.SumIfs(s.Columns(1), s.Columns(2), "J")
MsgBox e
End Sub
This also gives me what i want.
Related
I have a very simple SUMIF that looks something like this
lngth = Cells(Rows.Count, 9).End(xlUp).Row
For i = 2 To lngth
Range("L" & i & ":L" & lngth) = Application.WorksheetFunction.SumIf(Range("Sheet2!A$2:A$1000"), Cells(i, 9), Range("H$2:H$1000"))
Next i
Unfortunately I no longer have the data range available. However the information is now presented in an array. How would I go about adjusting the SUMIF to use an array instead of the ranges?
I don't think you can. Sumif is expecting range and if you pass array into it, you get type mismatch. I wrote this Sumif replacement you can try using.
Prerequisite for testing:
Range A1:B6 looking like this.
A
B
a
1
a
1
b
1
c
1
d
1
a
1
Code:
Sub test()
Dim arr() As Variant
arr = Range("A1:B6")
Debug.Print mySumif(arr, "a", 1, 2)
End Sub
Function mySumif(ByVal arr As Variant, ByVal criteria As Variant, ByVal criteriaColNo As Integer, ByVal sumColNo As Integer) As Double
For i = LBound(arr) To UBound(arr)
If arr(i, criteriaColNo) = criteria Then mySumif = mySumif + arr(i, sumColNo)
Next i
End Function
Output:
3
How to use:
arr is the array, criteria is what you're looking for, criteriaColNo is the index of the column your criteria is in, sumColNo is the index of the column you want to sum.
I have an array like this :
myColumns = Array("Serial","Practice","Manager", "QTD")
But I want to fetch its values from a sheet to make it more dynamic. (The values & their number may vary)
So I tried this to affect the range from A2 to last value of the column to my array:
myColumns = Range(Range("A2"), Range("A2").End(xlDown)).Value
It results in :
UBound(myColumns) -> 4 -> OK
BUT when I do this :
s = myColumns(3) -> Subscribe out of range !
How is that possible?
How can I populate it correctly?
Thank you!
Application.Transpose is a good friend of yours, if you are parsing a single column:
Sub TestMe1()
Dim myArr As Variant
myArr = Application.Transpose(Range("A1:A10"))
Dim cnt As Long
For cnt = LBound(myArr) To UBound(myArr)
Debug.Print myArr(cnt)
Next cnt
End Sub
If you are parsing a single row, you should transpose twice:
Sub TestMe2()
Dim myArr As Variant
With Application
myArr = .Transpose(.Transpose(Range("A1:AI1")))
End With
End Sub
If you are simply parsing multiple range or without .Transpose(), you have to refer to both columns and rows:
An article I wrote about it some time ago.
I'm writing a sub in VBA that is trying to look at each element in one array and see if it shows up in another array. The first array is in rows A2:A325 in Sheet A, and the second array is over 250,000 values. I keep getting a runtime error 9: subscript out of range. My code is below
Private Sub ICD_DRG_Converter()
Dim StudyDRG() As Variant
Dim StudyICD10() As Variant
Dim element As String
Dim lLastRow, i, j, k As Long
Dim ICD10Code As String
Worksheets("Accepted DRG's").Activate
ReDim StudyDRG(1 To 325) As Variant
StudyDRG = Range("A2:A325") 'Populate the study DRG's into an array for comparison
Worksheets("full_appendix_B").Activate
lLastRow = ActiveSheet.Cells(Rows.Count, "B").End(xlUp).Row 'get the last row of data for sizing our ICD 10 array
ReDim StudyICD10(1 To (lLastRow)) As Variant
StudyICD10 = Range("B2:B" & lLastRow)
'i = 0
For i = LBound(StudyICD10) To UBound(StudyICD10)
k = 1
For j = LBound(StudyDRG) To UBound(StudyDRG)
If StrComp(StudyICD10(i), StudyDRG(j), vbBinaryCompare) = 0 Then 'match between study DRG and ICD-10 DRG
Worksheets("full_appendix_B").Activate
ICD10Code = Range("A" & j).Value
Worksheets("Accepted ICD-10").Activate
Range("A" & k) = ICD10Code
k = k + 1
Exit For
End If
Next j
Next i
End Sub
The line that generates the error is:
If StrComp(StudyICD10(i), StudyDRG(j), vbBinaryCompare) = 0 Then
Any help on how to fix this would be appreciated. I've tried everything I know
When you use Range() to return a range of values into a variant array, the array is resized to match the range. So the results of
ReDim StudyDRG(1 To 325) As Variant
StudyDRG = Range("A2:A325")
is that studyDRG will have elements from 1 to 324, not 1 to 325.
Not only that, but Range() always returns a two dimensional array, even if there's only one column. So to refer to the element that corresponds to A2, you need to use StudyDRG(1,1), and A3 would be StudyDRG(1,2).
I hope this helps.
My VBA knowledge is very limited. I looked through the questions on StackOverflow and googled for a couple of days, but I couldn't find the solution to my problem.
So, I am working on an Excel macro. I have a range A3:H7136. Certain cells in column A have a value of 1; the rest are blank. Cells in columns D, E, F, G, H may be blank or may contain text or numbers.
What I am trying to do is take the range A3:H7136 and put the data into an array; exclude rows with blank A cells AND with blank D cells; convert to a "final" array, from where the data from columns 2, 4 and 8 will be pasted into ranges D309:D558, G309:G558, J309:J558 on another worksheet.
So far I've got the following:
Private Sub CommandButton1_Click()
Dim RowArray() As Long
Dim my_array1 As Range
Dim my_array2 As Variant
Dim i As Integer
Set my_array1 = ThisWorkbook.Worksheets("ETC").Range("A3:H7136")
my_array2 = my_array1.Value
For i = 1 To UBound(my_array2)
If my_array2(i, 1) = 1 And my_array2(i, 4) <> "" Then
RowArray(x) = i: x = x + 1
End If
Next i
Sheets("Allocation").Range("D309:D558") = Application.Index(my_array2, 1, Array(4))
Sheets("Allocation").Range("J309:J558") = Application.Index(my_array2, 1, Array(2))
End Sub
I stopped there because I realized that the code pastes #value! into the ranges on another worksheet. This code is "Frankenstein-ed" from several forums so it might look very weird to a professional. I need help getting the code to work. I also have several questions:
If the "final" array is 100% blank (which can happen), how do I get rid of #Value! on another worksheet?
In the last two rows it looks to me like I am using the original my-array2, and not the "final" filtered version of it. Should I declare the "final" array?
My paste range is only 250 rows; there is no way the number of non-blank rows in the array will ever exceed 250 rows, however, will that difference be a problem?
Thanks in advance!
A couple things:
RowArray's size was never declared so it would throw an out of bounds error.
You can use three array for the outputs in the loop then directly assign the arrays to the needed areas.
Private Sub CommandButton1_Click()
Dim DArray() As Variant
Dim GArray() As Variant
Dim JArray() As Variant
Dim my_array2 As Variant
Dim i As Long, x As Long
Dim cnt As Long
cnt = ThisWorkbook.Worksheets("ETC").Evaluate("COUNTIFS(A3:A7136,1,D3:D7136,""<>"")")
If cnt > 0 Then
ReDim DArray(1 To cnt, 1 To 1) As Variant
ReDim GArray(1 To cnt, 1 To 1) As Variant
ReDim JArray(1 To cnt, 1 To 1) As Variant
my_array2 = ThisWorkbook.Worksheets("ETC").Range("A3:H7136").Value
x = 1
For i = 1 To UBound(my_array2)
If my_array2(i, 1) = 1 And my_array2(i, 4) <> "" Then
DArray(x, 1) = my_array2(i, 4)
GArray(x, 1) = my_array2(i, 4)
JArray(x, 1) = my_array2(i, 8)
x = x + 1
End If
Next i
Sheets("Allocation").Range("D309").Resize(UBound(DArray, 1), 1).Value = DArray
Sheets("Allocation").Range("G309").Resize(UBound(GArray, 1), 1).Value = GArray
Sheets("Allocation").Range("J309").Resize(UBound(JArray, 1), 1).Value = JArray
End If
End Sub
I am experimenting with something:
There is a list with names, and what I would like to do, is to read the cell values in an array (this part works) than run a check for every cell in the worksheet and if a given cell is the same as a string inside an array, do something.
But unfortunatly I get the "type mismatch" error.
Ps. I know this doesn't make much sense and I could to that something inside the server function, but belive me I have my reasons. :-)
Edit: fixed a few things, now it looks like this (now I get the object doesn't support this property of method)
If it helps, you can also try it. You just need to add a cell with the name "Servers" and under it write some random words. Right now it should write in msgbox "ok" x times, where x is the number of rows you wrote in, under the cell, named "Servers"
1
'server name
Function server(ByVal issrvname As String)
Dim j As Integer
Dim c As Range
Dim x As Integer, y As Integer
For Each c In Sheets("Topology").UsedRange.Cells
Dim srvname() As String
j = 0
If c.Cells.Value = "Servers" Then
y = c.Column: x = c.Row + 1
Do Until IsEmpty(Cells(x, y))
ReDim Preserve srvname(0 To j) As String
srvname(j) = Cells(x, y).Value
x = x + 1
j = j + 1
Loop
End If
Next c
For Each c In Sheets("Topology").UsedRange.Cells
If IsInArray(c.Cell.Value, srvname) Then
issrvname = True
Else
issrvname = False
End If
Next c
End Function
2
Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
IsInArray = (UBound(Filter(arr, stringToBeFound)) > -1)
End Function
3
Sub test()
Dim c As Range
For Each c In Sheets("Topology").UsedRange.Cells
If server(c) = True Then
MsgBox "ok"
End If
Next c
End Sub
I think you can condense your functions:
First you need to include your Array generating block to your main sub.
Including it in the Function server is slowing code execution because it needs to generate the array in every call of the server Function
Edit1: This is tried in tested now. I've re-written your function and improve your sub a bit.
Sub test()
Dim j As Integer
Dim c As Range, c1 As Range
Dim x As Integer, y As Integer
Dim i As Long '~~> added it just to check how many is shown in MsgBox
For Each c In Sheets("Topology").UsedRange.Cells
'~~> generate array if "Servers" is encountered
If c.Value = "Servers" Then
Dim srvname() As String
j = 0
y = c.Column: x = c.Row + 1
With Sheets("Topology").UsedRange
Do Until IsEmpty(.Cells(x, y))
ReDim Preserve srvname(j)
srvname(j) = .Cells(x, y).Value
x = x + 1
j = j + 1
Loop
End With
'~~> use the generated Array of values here
i = 1
For Each c1 In Sheets("Topology").UsedRange.Cells
If IsInArray(c1.Value, srvname) Then
MsgBox "ok" & i
i = i + 1
End If
Next c1
End If
Next c
End Sub
Here's the new function: (actually, you don't need it, you can call the Match function directly in main Sub)
Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
IsInArray = Not IsError(Application.Match(stringToBeFound, arr, 0))
End Function
Maybe you do this just for testing? I just thought that the sheet you use to generate the array must be different from the sheet you want to compare the server names.
I think it might be that you define c as a range in Test, but call server with c when server is expecting a boolean.