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
Related
I'm currently in the process of solving a
type mismatch error
in a macro I'm writing, and I've written a short subroutine to drill down on the specific issue. This subroutine should loop through all of Column A, entering the numbers 1-10 in rows 1-10.
Sub looptest()
Dim rRange As Range
Dim rCell As Range
Dim i As Integer
Set rRange = ThisWorkbook.Worksheets(1).Columns(1)
i = 0
For Each rCell In rRange
If i < 10 Then
i = i + 1
rCell.Value2 = i
End If
Next rCell
End Sub
Instead this fills every cell in Column A with 1. Stepping through it in debug mode shows that instead of referencing a single cell, rCell references the entire column.
I have found that if I replace
Set rRange = ThisWorkbook.Worksheets(1).Columns(1)
with
Set rRange = ThisWorkbook.Worksheets(1).Range("A1:A100")
the macro works as intended, however I'd prefer to be able to use Columns() or something similar in my production code.
Am I using the Columns() property improperly, and if so, is there a good workaround?
Begin with these changes:
Set rRange = ThisWorkbook.Worksheets(1).Columns(1).Cells
Dim i As Long
Using the .Cells lets us loop over the cells in a column rather than columns in a worksheet.
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.
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've searched online and found a few solutions, but none of them make sense to me. I'm wondering why this specifically doesn't work:
Dim rng As Range: Set rng = Range("A5:A10")
For Each cell In rng
Dim contents As String: contents = ThisWorkbook.Sheets("ROI's").Range("cell").Value
MsgBox (contents)
Next cell
(BTW this is within a larger macro which works)
It keep saying that the error is on the third line
In addition to Scott Craners answer, take the parenthesis away from around contents in MsgBox (contents), you are not placing it into a variable so it should not be enclosed.
Sub try2()
Dim rng As Range
Dim cell As Range
Dim contents As String
Dim ws As Worksheet
Set ws = Worksheets("Sheet1")
Set rng = Range("A1:A10")
For Each cell In rng
contents = ws.Range(cell.Address(0, 0)).Value
MsgBox (contents)
Next cell
End Sub
I've been practicing wtih various problems concerning VBA...the above is just a snippet synthesizing what all the fine people above me have said about making this work. My 2 cents, brackets or not around the contents variable, the result is the same.
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