2
\$\begingroup\$

I am converting below excel formula to VBA code and it works as required.

=IFERROR(IF(A2="NULL",B2*C2,A2*B2*C2),0)

Could someone please review and suggest if any changes are required in terms of handling errors.

What the code does : If there is an error it returns '0' else executes the code.

Sub MultiplyValues()
'
' MultiplyValues Macro
'
' =IFERROR(IF(A2="NULL",B2*C2,A2*B2*C2),0)
' Forumala is in D

Dim rCell As Range
Dim rRng As Range
Set rRng = Range("A2", Range("A2").End(xlDown))
    For Each rCell In rRng.Cells
        On Error GoTo ErrHandler:
            If rCell.Value = "NULL" Then
                rCell.Offset(0, 3) = rCell.Offset(0, 1) * rCell.Offset(0, 2)
            Else
                rCell.Offset(0, 3) = rCell.Offset(0, 0) * rCell.Offset(0, 1) * rCell.Offset(0, 2)
            End If
            Next rCell
         Exit Sub
         ErrHandler:
            rCell.Offset(0, 3) = 0
    Resume Next
End Sub
\$\endgroup\$
2
\$\begingroup\$

This is easily handled with a simple function that checks for a potential error instead of triggering an error. Code first, and then an explanation:

Public Sub MultiplyValues()
    '
    ' MultiplyValues Macro
    '
    ' =IFERROR(IF(A2="NULL",B2*C2,A2*B2*C2),0)
    ' Forumala is in D

    Dim Target As Range

    ' The line below implicitly uses the ActiveSheet. If this is intended, qualify your range with `ActiveSheet.Range`, otherwise
    ' you will want to identify the proper sheet.
    Set Target = Range("A2", Range("A2").End(xlDown))
    Dim Cell As Range
    For Each Cell In Target.Cells
        ' No need to use an error handler. Avoiding errors is always preferred over relying on the error handler. In this case, let's use a function.
        Cell.Offset(0, 3).Value = GetFormulaValue(Cell)
    Next Cell
End Sub

Private Function GetFormulaValue(ByVal InputCell As Range) As Double
    If Not IsNumeric(InputCell.Value) Then
        ' Be sure to explicitly access the value property of the range object.
        GetFormulaValue = InputCell.Offset(0, 1).Value * InputCell.Offset(0, 2).Value

    ' I am adding this in case you want to run a separate calculation if the cell-value is 0).
    ' ElseIf InputCell.Value = 0 Then
        ' Your code here
    Else
        ' While order of operations would automatically calculate X * Y and then XY * Z, I prefer using parentheses
        ' to make my code even clearer.
        GetFormulaValue = (InputCell.Offset(0, 0) * InputCell.Offset(0, 1)) * InputCell.Offset(0, 2)
    End If
End Function

Public vs Private

First I would like to note the use of a Public subroutine along with a Private function. This basically just means that the MultiplyValues subroutine is accessible by other routines/the macro's list. On the other hand, the function is only seen by other code in the same module (so we don't risk the wrong code using it, and potentially returning a bad value). This is a little bit more advanced compared to where you are, but it is a good practice to get into early.

Variable Declarations

I noticed that you were using Hungarian Notation (HN). I would venture to assume we all go through this phase in the SO VBA community, but the sooner you break that habit, the better. It is far better to properly name variables than to use HN to denote the types. For this purpose, the names were easy. For larger projects it can be a bit more complicated. The sooner you avoid the habit though, the better.

The Good Stuff

Avoiding Errors Before They Happen

Error handling in VBA is not very robust. It works, but oftentimes it is misused. I am of the mindset that error handling (in the line label sense) should be avoided at all costs. The best way to accomplish this is to use logic to your advantage, and to wrap repeated operations in a single function.

In this case, the appropriate method was easy. The function takes an input cell, and returns a value based on the contents of the cell. While you initially checked for Null the function checks for IsNumeric since this will catch Null as well as anything else that isnt a number (since XYZ isn't Null but will throw an error if multiplied by a number).

Why Use Functions?

I like to think of functions as magical creatures that really are under-appreciated by newer programmers. Maybe I am hyping them up a bit. Maybe not. What I can say for sure is I used to avoid them. Then, someone came along (cough ThunderFrame cough) and lightly-reprimanded my avoidance of using single-functions for repeated steps (I had the same Format(X, Y) command in numerous places. He reminded me that, should that command ever change, I would then have to find every single place it lived and fix it. Simple advice, simple reminder, but it changed my approach to using Functions.

I tend to ramble, so the point here is this: When you have some operation that needs to be done in multiple places (or on multiple conditions) you should strongly consider a function. That way, should the operation change, you can fix it in the function, and not in all of the places that you are calculating the operation manually. Functions also allow you to return different types of values. Declare a Function as a Variant and you can return a vbNullString a 0 a "ThisIsABadValuePleaseIgnoreIt". Whatever you want to do. They allow greater control over the flow of your code.

Final Tidbits

As I do in many of my CR posts, I recommend using RubberDuck, especially when you're starting out. You can find it here: http://rubberduckvba.com/. It helps for finding those little things you do without knowing it (Implicit reference to ActiveWorksheet and .Value for example) and help you learn better habits. Over time, these small changes in habits can make or break a programmer.

Best of luck on this.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.