VBA Average Function Error - 1004 - vba

I am getting an error 'Unable to get average property of Worksheetfunction class' when attempting to calculate the average of 2 ranges.
My table can be found below.
When I calculate the average of the % column, I have no problems. It provides me with the correct output, however, I have problems when I try and calculate the average for the $ column.
Col | % | $
1 | 2.33% | $2.33
2 | 3.64% | $3.64
3 | 10.83% | $10.83
4 | 6.07% | $6.07
5 | - | -
6 | 12.99% | $12.99
7 | 18.99% | $18.99
Dim myRange As Range
Dim myAverage As Variant
'The user selects the range
Set myRange = Application.InputBox( _
prompt:="Please select the range", Title:="My Range", Type:=8)
'This splits the range into two areas because the user typically does not select the row with the "-" in it.
'myRange would typically look something like (B1:B4,B6:B7) OR (C1:C4,C6:C7)
Area1 = myRange.Areas(1)
Area2 = myRange.Areas(2)
myAverage = Application.WorksheetFunction.Average(Area1, Area2)
The error I receive is 'Unable to get average property of Worksheetfunction class' and it happens with the myAverage calculation.
Any ideas as to why it calculates the % column without problem, but doesn't calculate the $ column?
Thanks in advance!

Declare as the proper type (Range object):
Dim Area1 As Range
Dim Area2 As Range
Use the Set keyword to assign to Object variables.
Set Area1 = myRange.Areas(1)
Set Area2 = myRange.Areas(2)
Then, you should be able to use the Application.Average or Application.WorksheetFunction.Average to get the mean.
myAverage Application.Average(Area1, Area2)
Otherwise, you're passing a variant array to the function, which is not supported, hence it raises the error. The Average function requires either a contiguous range of multiple cells, or itemized list of individual cells or values. You can't pass it multiple ranges of multiple cells each.
Or, omit the Area1/Area2 steps entirely and do simply:
myAverage = Application.Average(myRange.Areas(1), myRange.Areas(2))

Related

Passing data from column

I have userform with three fields (COMNAME; COMCOMPANY; TXTVALUE) I tried to pass value from column F to TXTVALUE (textbox) when COMNAME and COMCOMPANY (combo boxes) values are matched row values in columns C and D.
Dim var1 As Integer
Dim var2 As Integer
With Application.WorksheetFunction
var1 = .Match(Me.COMCOMPANY.Value, sheet.range("C7:C10"), 0)
var2 = .Match(Me.COMNAME.Value, sheet.range("D7:D10"), 0)
TXTVALUE.Value = .Index(sheet.range("F7:F10"), var1, var2)
End With
Table looks like this
column C |column D |Column F
PA | CT | 750
RS | HA | 550
PA | CS | 358
When i execute this macro the result in txtvalue is good if is select PA (COMCOMPANY) and CT (COMNAME) - the TXTVALUE is 750 but when I select PA (COMCOMPany) and CS (COMNAME) - the macro return error "Unable to get index property of WorksheetFunction Class".
How to make this code or code like this to work. Thank you
You seem to have some confusion as to how the index function (and possibly the match function) works.
The match function works like this:
MATCH(x, rng, intExact)
means to find the FIRST value x in the range rng and the intExact parameter is defining whether you want the first value less than, exactly equal to, or more than the value x. In your case intExact = 0 and you are searching for values exactly equal. This function will return the row number that the first match is found, beginning with the top of the range.
Assuming that you have displayed cells D6:F9 in your case var1 will return 1 for PA. Var2 will return 1 for CT and 3 for CS. There is no relation between the two variables. Var1 is searching in the D column for your input, and Var2 is searching in the F column for your second input.
The INDEX function works like this:
INDEX(rng, lngRow, lngCol)
means to go in the rng and get the specified row, lngrow and column lngcol starting from the upper left part of the range. In your first case, you select row 1 and column 1 in range F7:F10, which happens to be 750. It is a coincidence that this works, because the match in the E column happens to be in the first row.
In your second case, you try to select the value in row 1 and column 3, but there is only 1 column in your range, so you get an error.
You need to combine this with a lookup column.
column C |column D |Column F | Column Z
PA | CT | 750 | PACT
RS | HA | 550 | RSHA
PA | CS | 358 | PACS
And use the function:
Var1 = .Match(Me.COMCOMPANY.Value & Me.COMNAME.Value, _
sheet.range("Z7:Z10"), 0)
TXTVALUE.Value = .Index(sheet.range("F7:F10"), var1)
You can omit the column parameter of the Index function.
If you are unsure what is happening, it is best to step through the code, noting what the variables are. In the case of the WorksheetFunction function you can then actually put the function in a cell and supply the values that the variables are to troubleshoot the issue.
Use Application.Match function to avoid possible errors being thrown and handle them in the returned variable of Variant type
Dim var1 As Variant
Dim var2 As Variant
With Application
var1 = .Match(Me.COMCOMPANY.Value, sheet.range("C7:C10"), 0)
If Not IsError(var1) Then
var2 = .Match(Me.COMNAME.Value, sheet.range("D7:D10"), 0)
If Not IsError(var2) And var1 = var2 Then Me.TXTVALUE.Value = sheet.range("F7:F10").Rows(var1)
End If
End With

VBA fill cells by random numbers depending on other cells

I am working with Excel 2013 and I need to fill the range of cells by random numbers. To be exact, Range(B1:B4) fill by numbers in order to value of cells in previous column, I mean in Range(A1:A4). I really have no idea how to fill that using VBA if there is that condition, otherwise it's simple.
Here is a scetch of cells
# | A | B |
----------------------
1 | Yes | 1 |
----------------------
2 | No | 2 |
----------------------
3 | Maybe | 3 |
----------------------
4 | No | 2 |
----------------------
If all you need is random numbers, you don't need VBA. Just set your cell formula equal to:
"=RANDBETWEEN(1,3)"
However, your random numbers will change every time your worksheet is calculated. To avoid this, you can define the following sub and associate it, for example, with an action button:
Sub makeRand()
Dim targetRange As Range
Dim xlCell As Range
Dim upperBound As Integer
Dim lowerBound As Integer
Set targetRange = Range("B1:B4")
upperBound = 3
lowerBound = 1
Randomize
For Each xlCell In targetRange
xlCell.Value = Int((upperBound - lowerBound + 1) * Rnd + lowerBound)
Next xlCell
End Sub
If all you need to do is set this with vba, this should give you values 1, 2, or 3:
Range("B1:B4").Formula = "=RANDBETWEEN(1,3)"
If you only need an Excel formula, you can always just paste =RANDBETWEEN(1,3) into the formula bar.
If you're trying to define column B values based on column A values, just use:
Range("B1:B4").Formula = "=IF(A1 = ""Yes"", 1, IF(""No"", 2, If(""Maybe"", 3, ""ERROR"")))"
If neither of those are what you want, you're going to have to clarify better.

Is there a coalesce-like function in Excel?

I need to fill a cell with the first non-empty entry in a set of columns (from left to right) in the same row - similar to coalesce() in SQL.
In the following example sheet
---------------------------------------
| | A | B | C | D |
---------------------------------------
| 1 | | x | y | z |
---------------------------------------
| 2 | | | y | |
---------------------------------------
| 3 | | | | z |
---------------------------------------
I want to put a cell function in each cell of row A such that I will get:
---------------------------------------
| | A | B | C | D |
---------------------------------------
| 1 | x | x | y | z |
---------------------------------------
| 2 | y | | y | |
---------------------------------------
| 3 | z | | | z |
---------------------------------------
I know I could do this with a cascade of IF functions, but in my real sheet, I have 30 columns to select from, so I would be happy if there were a simpler way.
=INDEX(B2:D2,MATCH(FALSE,ISBLANK(B2:D2),FALSE))
This is an Array Formula. After entering the formula, press CTRL + Shift + Enter to have Excel evaluate it as an Array Formula. This returns the first nonblank value of the given range of cells. For your example, the formula is entered in the column with the header "a"
A B C D
1 x x y z
2 y y
3 z z
I used:
=IF(ISBLANK(A1),B1,A1)
This tests the if the first field you want to use is blank then use the other. You can use a "nested if" when you have multiple fields.
Or if you want to compare individual cells, you can create a Coalesce function in VBA:
Public Function Coalesce(ParamArray Fields() As Variant) As Variant
Dim v As Variant
For Each v In Fields
If "" & v <> "" Then
Coalesce = v
Exit Function
End If
Next
Coalesce = ""
End Function
And then call it in Excel. In your example the formula in A1 would be:
=Coalesce(B1, C1, D1)
Taking the VBA approach a step further, I've re-written it to allow a combination of both (or either) individual cells and cell ranges:
Public Function Coalesce(ParamArray Cells() As Variant) As Variant
Dim Cell As Variant
Dim SubCell As Variant
For Each Cell In Cells
If VarType(Cell) > vbArray Then
For Each SubCell In Cell
If VarType(SubCell) <> vbEmpty Then
Coalesce = SubCell
Exit Function
End If
Next
Else
If VarType(Cell) <> vbEmpty Then
Coalesce = Cell
Exit Function
End If
End If
Next
Coalesce = ""
End Function
So now in Excel you could use any of the following formulas in A1:
=Coalesce(B1, C1, D1)
=Coalesce(B1, C1:D1)
=Coalesce(B1:C1, D1)
=Coalesce(B1:D1)
If you know there will not be any overlap across columns, or want the overlap, then this is a pretty fast way to solve for a coalesce. The below formula does not apply to your values and columns, but rather to my mock-up so you will need to adjust to make it relevant.
=LEFT(TRIM(CONCATENATE(Q38,R38,S38,T38,U38,V38,W38,X38,Y38)),1)
With the updated IFS function in excel you don't need to nest. You can try something like
Create a blank cell to the right
Then enter.
=IFS(ISBLANK(A1),B1,ISBLANK(A1),C1,ISBLANK(A1),D1)
Highlight column and paste as needed.
If you only want to coalesce to 0, which is a very common use case, you can simply use the SUM() function around a single value. As a convenience that one treats all blanks as zero, and it's extra convenient since it's so short.
Not a generic solution like the other answers, but a helpful shortcut in many cases where that's exactly what you want.
Inside the array enter the variables that are not allowed.
Function Coalesce(ParamArray Fields() As Variant) As Variant
Dim v As Variant
For Each v In Fields
If IsError(Application.Match(v, Array("", " ", 0), False)) Then
Coalesce = v
Exit Function
End If
Next
Coalesce = ""
End Function
Depending on how many cells you want to check, you can chain together multiple ISBLANK checks.
For instance, when checking columns A, B, then C:
=IF(ISBLANK(A1),IF(ISBLANK(B1),C1,B1),A1)
For columns A, B, C, and D:
=IF(ISBLANK(A1),IF(ISBLANK(B1),IF(ISBLANK(C1),D1,C1),B1),A1)
... and so on.
Late to the party and leveraging #AndyMC's answer you can use the following to evaluate vlookups, index(match()) etc. to coalesce your formula statements.
Public Function Coalesce(ParamArray Fields() As Variant) As Variant
Dim v As Variant
For Each v In Fields
If Not IsError(v) Then
Coalesce = v
Exit Function
End If
Next
Coalesce = CVErr(xlErrNA)
End Function
and use it in your Worksheet as followed: =Coalesce(INDEX(SHEET1!$A:$AF,MATCH(Main!$Q36,SHEET1!$I:$I,0),MATCH(Main!D$34,SHEET1!$1:$1,0)),INDEX(SHEET2!A:CR,MATCH(Main!$Q36,SHEET2!$M:$M,0),MATCH("SOMETHING",SHEET2!$1:$1,0)))
For the first statement that does not return #N/A it will return the actual matched value.

VBA - Index / Match function with multiple criteria

I am facing an issue when trying to compile a multiple criteria Index/Match code in VBA. This might be simple - but i am fairly new to VBA and nothing i have found around here worked.
Example:
I have a large amount of data in a specified range: Sheets("CustomerAccounts").Range(CustomerSheetRange)) - I need VBA to return data from column titled "Values" by checking three criteria: Customer = X, Type = External, OriginCountry = UAE (columns are not adjacent in the original spreadsheet)
The criteria are stored in separate variables set by user of the macro beforehand.
Customer | Type | Origin | Destination | Values
X | Internal | UAE | SA | Value 1
Y | Internal | UAE | SA | Value 2
X | External | UAE | SA | Value 3
X | External | ZA | UAE | Value 4
At the moment i have the following (quite bulky) code which finds the value using one criteria - OriginCountry variable.
The code searches for it in a pre-specified column - OriginCountryColumn.
ResultString = Application.Index(Sheets("CustomerAccounts").Range(CustomerSheetRange), Application.Match(OriginCountry, Sheets("CustomerAccounts").Range(OriginCountryColumn), 0), Application.Match("Values", Sheets("CustomerAccounts").Range(TitleRowCust), 0))
I would like to modify the code to also match the Type and The customer.
Is it possible to expand the above Index/Matxh function - or should i use a different approach?
Any advice is appreciated.
You may walk through rows checking matches:
Dim row as Long
With Sheets("CustomerAccounts").Range(CustomerSheetRange))
For row = 2 To .Rows.Count 'Starts in 2 to ignore header!
If .Cells(row, costumerCol).Value Like costumerCriteria And .Cells(row, typeCol).Value Like typeCriteria And .Cells(row, originCol).Value Like originCriteria Then
'This is a match!
Debug.Print .Cells(row, valueCol)
End if
Next
End With
You must replace costumerCol, typeCol, originCol and valueCol with corresponding column number and costumerCriteria, typeCriteria and originCriteria with criteria specified.
If column indexes are also variable, make a search for them in first row before walking through rows.
First, format the range containing your data to a Table (See http://office.microsoft.com/en-001/excel-help/quick-start-create-an-excel-table-HA010359200.aspx on how to do that). Once done, use the following VBA code:
SomeCustomer = Range("...").Value
SomeType = Range("...").Value
SomeOrigin = Range("...").Value
ActiveSheet.ListObjects("Table1").Range.AutoFilter Field:=1, Criteria=SomeCustomer
ActiveSheet.ListObjects("Table1").Range.AutoFilter Field:=2, Criteria=SomeType
ActiveSheet.ListObjects("Table1").Range.AutoFilter Field:=3, Criteria=SomeOrigin
Notes:
You might have to customize this macro for your specific needs
Name of table can be found/modified through Formulas>Name Manager
ActiveSheet might be modified to the actual sheet you are using

Autofill from a named range to a unnamed destination in Excel using VBA

I am trying to autofill from a named range to its right neighbour cells. The original range contains combined cells and is 4x4 cells in size. My problem is that the Autofill-method needs a Destination:=Range("...") and does not take something like Destination:=xlSameAreaToTheRight.
The solution would be if I could do something like
Range("origin").AutoFill Destination:=( Range("origin").Area + Rows(4) ), _
Type:=xlFillDefault
So how can I find the rows and cols of a named range in the format A1:D4 and add like 4 cols to it?
Thnks for your support!
I am not sure what you want to get as result, but here are the answers to your particular questions:
You can get the Range as String in the format $A$1:$D$4 using .Address, and to get the amount ofthe columns just add .Columns.Count to the named range. (Using offset is should be no problem to add the amount of the columns to the active cell within your range.)
Dim TestRange As String
Dim AmountColumns As Integer
TestRange = ActiveWorkbook.Sheets(1).Range("Test").Address
AmountColumns = ActiveWorkbook.Sheets(1).Range("Test").Columns.Count
Thanks to stema's answer I found a (quite simple) solution:
To add rows or cols to a named range and use it as AutoFill destination one can simply do
Range("origin").AutoFill Destination:=( Range("origin").Range("A1:H2") ), _
Type:=xlFillDefault
what would autofill four columns and two rows to our "origin":
| 1 | 2 |
| 1,1 | 1,2 | 2,1 | 2,2 |
This leads to:
| 1 | 2 | 3 | 4 |
| 1,1 | 1,2 | 2,1 | 2,2 | 2,7 | 3,12 | 3,54 | 3,96 |
Here's one way to use #stema's suggestion.
Sub namedfill()
Dim locOrig As String
Dim firstCell As String
Dim lastCell As String
'Pluck address of top-left and bottom-right cells from .Address string
locOrig = Range("Origin").Address
firstCell = Left(locOrig, 4)
lastCell = Right(locOrig, 4)
Range("Origin").AutoFill Destination:=Range(firstCell, Range(lastCell).Offset(0, 4)), Type:=xlFillDefault
End Sub