How would one go about changing this formula of sumifs into an VBA macro.
=SUMIFS('Staff Allocation'!N3:N6,'Staff Allocation'!B3:B6,Sheet2!A1,'Staff Allocation'!E3:E6,Sheet2!B1,'Staff Allocation'!F3:F6,Sheet2!C1)
+SUMIFS(Modules!H2:H4,Modules!B2:B4,Sheet2!A1,Modules!G2:G4,Sheet2!B1,Modules!E2:E4,Sheet2!C1)
So it can be used in an if formula not =0 then do a specified action else move to the next process.
A1 is the first criteria value,
B1 is the second criteria value,
C1 is the third criteria value
You use Application.WorksheetFunction to access worksheet functions.
According to the documentation for SumIfs you need to provide your arguments as Range objects.
So you need to take each range you want to evaluate and declare it as a range object. In Excel, the Range object is a member of the Worksheet object so you need to specify which worksheet the range is on. You can do this by specifying the name or index number from the Sheets Collection.
E.g. Sheet2!A1 can be obtained using SomeRange = Sheets("Sheet2").Range("A1")
Now, the Worksheet object is a member of the Workbook Object, so you can also specify what workbook you want - but if you read through the documentation links above, you'll see that the default for the Sheets collection is the ActiveWorkbook - so we'll rely on this to keep the example simple.
So to pass all your SumIfs arguments you could do something like below.
Granted this might not be the most elegant solution, I think it should be easy enough to understand how it works.
Sub Foo()
Dim Arg1 as Range
Dim Arg2 as Range
Dim Arg3 as Range
Dim Arg4 as Range
Dim Arg5 as Range
Dim Arg6 as Range
Dim Arg7 as Range
Dim Arg8 as Range
Dim Arg9 as Range
Dim Arg10 as Range
Dim Arg11 as Range
Dim Arg12 as Range
Dim Arg13 as Range
Dim Arg14 as Range
Set Arg1=Sheets("Staff Allocation").Range("N3:N6")
Set Arg2=Sheets("Staff Allocation").Range("B3:B6")
Set Arg3=Sheets("Sheet2").Range("A1")
Set Arg4=Sheets("Staff Allocation").Range("E3:E6")
Set Arg5=Sheets("Sheet2").Range("B1")
Set Arg6=Sheets("Staff Allocation").Range("F3:F6")
Set Arg7=Sheets("Sheet2").Range("C1")
Set Arg8=Sheets("Modules").Range("H2:H4")
Set Arg9=Sheets("Modules").Range("B2:B4")
Set Arg10=Sheets("Sheet2").Range("A1")
Set Arg11=Sheets("Modules").Range("G2:G4")
Set Arg12=Sheets("Sheet2").Range("B1")
Set Arg13=Sheets("Modules").Range("E2:E4")
Set Arg14=Sheets("Sheet2").Range("C1")
If Application.WorksheetFunction.SumIfs(Arg1,Arg2,Arg3,Arg5,Arg6,Arg6,Arg7)
+ Application.WorksheetFunction.SumIfs(Arg8,Arg9,Arg10,Arg11,Arg12,Arg13,Arg14) > 0 Then
'Do Something
Else
'Do Something Else
End IF
End Sub
Related
I have a Named Range col_9395 it is an entire column. I want to set a range within this Named range. I want the range to start at row 3 to row 200 of same column. Whats the best way to do this?
Original working line without Named Range:
Set rngBlnk = Sheet108.Range("T3:T200").SpecialCells(xlCellTypeBlanks)
This is the code I tried with no luck:
Set rngBlnk = Range("col_9395)(3,1):Range("col_9395)(200,1).SpecialCells (xlCellTypeBlanks)
Might be wrong, but I find this the easiest one:
Private Sub Test()
Dim rngBlnk As Range
Set rngBlnk = Range("col_9395").Rows("3:200").SpecialCells(xlCellTypeBlanks)
End Sub
You can see the sort of logic with
Option Explicit
Sub test()
Dim colToUse As Long
colToUse = ThisWorkbook.Worksheets("Sheet1").Range("ol_9395").Column
With ThisWorkbook.Worksheets("Sheet1")
Debug.Print .Range(.Cells(3, colToUse), .Cells(200, colToUse)).Address
End With
End Sub
Sub t()
Dim rng As Range
Set rng = Range("col_9395") ' for easier use
Dim blnkRng As Range
Set blnkRng = Range(Cells(rng.Rows(3).Row, rng.Column), Cells(rng.Rows(200).Row, rng.Column)).SpecialCells(xlCellTypeBlanks)
blnkRng.Select
End Sub
What I did was assign your named range to a variable (just for easier referencing). Then using the Range() property, I used the 3rd and 200th row of your named range to set the range to look for blank cells.
The idea is that this will help you in the event your named range isn't simply an entire column. It will get the relative 3rd and 200th row from your named range.
Option Explicit
Public Sub TestMe()
Dim rngBlnk As Range
Dim firstCell As Range
Dim lastCell As Range
Set firstCell = [col_9395].Cells(3, 1)
Set lastCell = [col_9395].Cells(200, 1)
If WorksheetFunction.CountBlank(Range(firstCell, lastCell)) > 0 Then
Set rngBlnk = Range(firstCell, lastCell).SpecialCells(xlCellTypeBlanks)
End If
End Sub
A kind of a problem with SpecialCells and assigning to them is that if there are no cells from the specific type, it throws an error.
Thus, there is a check with WorksheetFunction.CountBlank()>0, before the setting of rngBlnk to the special cells.
I'm late to the party, I know, but maybe this will help someone. I've stumbled on a technique that I don't see presented as an option in any of the handful of "range-in-a-range" questions here.
I discovered you can ask VBA for a range of a range directly. I have formatted some data as a Table, but it could be simply a Named Range, or even an unnamed range I suppose. My code that is working looks like this:
With Workbooks(Filename)
.Worksheets(tabName).Activate
.Worksheets(tabName).Range("SummaryBand").Range("B2:R2").Copy
End With
My Table is named SummaryBand, which due to a previous step was not necessarily always in the same absolute position on the spreadsheet, but I wanted an absolute Range within SummaryBand. In this example, "B2:R2" is the absolute position within the Table, with the top left cell of the Table being A1.
Say that I have a UDF function:
Dim arr() as variant, rng as range
set rng = some range
arr=rng
Function=worksheetfunction.sum(arr)
If rng contains errors a values, how would I go about removing them from the arrray and not from the sheet?
I'd rather not do For each-statement looping through ranges in rng. I'd rather be able to remove them from the array.
This is more a general questions than anything.
I would suggest to add them to a Collection, since these works better with dynamic size (you could also use Scripting.Dictionary. Then loop through the range, and add non-error cell values to the collection.
I know the example doesn't meet your requirement for not looping each element. But unless you have a specific reason to keep your data as a range, I don't really see an advatage of this. There are other data structures, which are more suited for further processing of the data.
Simple example:
Sub FindNonErrorCells()
Dim rng As Range
Dim cell As Range
Dim coll As Collection
'Insert your range here
Set rng = Sheets("Sheet1").Range("C1:C5")
Set coll = New Collection
For Each cell In rng
If Not IsError(cell) Then
coll.Add cell.Value
End If
Next cell
End Sub
I am using follow code to find maximum value in a column. I need to know absolute reference number of where that value is found. I am hoping to use that absolute reference number in FOR loop and check what is in adjacent cells of where that value is found.
rng = Application.WorksheetFunction.Max(Columns("H"))
I have tried using match but I am getting error 2042.
adrs = Application.Match(rng, Range("H:H"), 0)
Is there a way to find out where that maximum value resides?
Please note I am searching for time stamps so wrapping rng in CLNG is not an option.
Or simply this?
Dim rng As Range
Set rng = Columns("H").Find(Application.Max(Columns("H")))
If Not rng Is Nothing Then
MsgBox rng.Address(0, 0)
End If
Try this:
Dim rng As Range
Dim maxValue As Integer
'you set your area that you get mxaimum value from
Set rng = Range("H:H")
'determine the maximum value
maxValue = Application.WorksheetFunction.Max(rng)
'select cell which contains found value
'(Find returns Range objects, so you can use it as you like)
rang.Find(maxValue).Select
You need to make sure that you are using the correct references.
For example, if the code is running off Sheet1 and the data is in Sheet2 then it is possible you will get errors. Trying this as an example I got the Error 2042 for the adrs variable when the code and the data were on different WorkSheets.
Also, always remember to Option Explicit this will force you to delcare your variables.
Try the following:
Option Explicit
Sub FindMaxValueAndReturnRowNum()
Dim SomeWorkSheet As Worksheet
'Here you can change the Sheet1 to that of you data sheet name
Set SomeWorkSheet = ThisWorkbook.Sheets("Sheet1")
Dim MaxNum As Long
Dim adrs As Long
'Notice the reference to where the code needs to evaluate
'if there is no reference to a specific sheet then it will use the active sheet.
MaxNum = Application.WorksheetFunction.Max(SomeWorkSheet.Columns("H"))
'adrs will return the row number
adrs = Application.Match(MaxNum , SomeWorkSheet.Range("H:H"), 0)
End Sub
I just started coding a few days back and am trying to use all dim variables, since that's what everyone has been saying to use. So, I am trying to clear formats using current region (basically all cells containing value or formatting). Here is my code and I get a compile error and VBA highlights the "Entire' portion of the last code. Any thoughts? I'm new and I can't figure out what I'm doing wrong.
Sub ClearFormatting()
Dim ws as Worksheet
Dim Entire As Range
Set ws = ThisWorkbook.Sheets("Sheet1")
Set Entire = Range("A1").CurrentRegion
ws.entire.ClearFormats
End Sub
Sub ClearFormatting()
Dim ws as Worksheet
Dim Entire As Range
Set ws = ThisWorkbook.Sheets("Sheet1")
Set Entire = ws.Range("A1").CurrentRegion
Entire.ClearFormats 'no ws.
End Sub
I'm new to Excel VBA. I am trying to use a VBA function I found online that enables the user to use goalseek on multiple cells at a time. How do I call the function in a spreadsheet and how do I point to the cells that are supposed to be associated with the variables in the function (e.g. Taddr, Aaddr, gval). Do I have to write the cell values and ranges in the code itself and just run it that way?
Maybe I should redefine the function so that it takes these variables as input, so I can write a formula like =GSeekA(Taddr,Aaddr,gval)
Option Explicit
Sub GSeekA()
Dim ARange As Range, TRange As Range, Aaddr As String, Taddr As String, NumEq As Long, i As Long, j As Long
Dim TSheet As String, ASheet As String, NumRows As Long, NumCols As Long
Dim GVal As Double, Acell As Range, TCell As Range, Orient As String
' Create the following names in the back-solver worksheet:
' Taddr - Cell with the address of the target range
' Aaddr - Cell with the address of the range to be adjusted
' gval - the "goal" value
' To reference ranges on different sheets also add:
' TSheet - Cell with the sheet name of the target range
' ASheet - Cell with the sheet name of the range to be adjusted
Aaddr = Range("aaddr").Value
Taddr = Range("taddr").Value
On Error GoTo NoSheetNames
ASheet = Range("asheet").Value
TSheet = Range("tsheet").Value
NoSheetNames:
On Error GoTo ExitSub
If ASheet = Empty Or TSheet = Empty Then
Set ARange = Range(Aaddr)
Set TRange = Range(Taddr)
Else
Set ARange = Worksheets(ASheet).Range(Aaddr)
Set TRange = Worksheets(TSheet).Range(Taddr)
End If
NumRows = ARange.Rows.Count
NumCols = ARange.Columns.Count
GVal = Range("gval").Value
For j = 1 To NumCols
For i = 1 To NumRows
TRange.Cells(i, j).GoalSeek Goal:=GVal, ChangingCell:=ARange.Cells(i, j)
Next i
Next j
ExitSub:
End Sub
GSeekA is a Subprocedure, not a Function. Subprocedures cannot be called from worksheet cells like Functions can. And you don't want to convert GSeekA into a function. Functions should be used to return values to the cell(s) from which they're called. They shouldn't (and often can't) change other things on the sheet.
You need to run GSeekA as a sub. Now the problem becomes how you get user provided information into the sub. You can use InputBox to prompt the user to enter one piece of information. If you have too many, InputBox becomes cumbersome.
You can create areas in the spreadsheet where the user must enter information, then read from that area. That's how it's set up now. It's reading cells named asheet and tsheet. As long as those named ranges are present, the code works.
Finally, you can create a UserForm that the user will fill out. That's like putting a bunch of InputBoxes on one form.
Update Here's a simple procedure that you can start with and enhance.
Public Sub GSeekA()
Dim rAdjust As Range
Dim rTarget As Range
Dim dGoal As Double
Dim i As Long
'Set these three lines to what you want
Set rAdjust = Sheet1.Range("I2:I322")
Set rTarget = Sheet1.Range("J2:J322")
dGoal = 12.1
For i = 1 To rAdjust.Count
rTarget.Cells(i).GoalSeek dGoal, rAdjust.Cells(i)
Next i
End Sub