简体   繁体   中英

Error in using 'For Each' loop and 'If' statement in excel vba

Excel VBA - Error in using 'For Each' loop and 'If' statement.

I have an excel workbook; which has two sheets:

The first sheet is: 'Jurisdictions' Which has three columns:

Country (Column B), State (Column C) and City (Column D)

This sheet has single entry for each city.

But, as each city is listed on separate row, the names of state and country(To which cities belong) can get repeated on multiple rows.

For Ex:

Col B   |  Col C   |   Col D   |
--------------------------------
U.S.    | New York |  Buffalo  |
U.S.    | New York | Manhattan |

(These are my two rows)

I have another sheet: Sheet1;

Here also I have same three columns; (and 20 some other columns)

These three columns I shall validate with three columns in 'Jurisdictions' sheet. (Only few 'Jurisdictions' are listed in Sheet1; and those can be in any order and can be for any country)

The validation Rules are:

1) For Country

  • Country name should be a single value only.
  • Should match with name under 'Country' column in Jurisdiction sheet.
  • Cases should be neglected (Uppercase/Lowercase)

2) State

  • Can have one or Multiple values separated only with semicolon (To separate those values with semicolon I have written a different code and it works fine)
  • The entry in this cell even can be 'All'
  • All the state names should match with states listed under 'state' column of Jurisdiction sheet. (If multiple entries are listed; those should be first separated based on delimeter - semicolon and then to be compared)
  • Cases should be neglected (Uppercase/Lowercase); Extra spaces before and after state names should be trimmed.

3) City

  • Can have one or Multiple values separated only with semicolon.
  • The entry in this cell can be 'All' also.
  • All the city names should match with cities listed under 'City' column of Jurisdiction sheet. (If multiple entries are listed; those should be first separated based on delimeter - semicolon and then to be compared)
  • Cases should be neglected (Uppercase/Lowercase); Extra spaces before and after state names should be trimmed.

I have the following code which validates Countries, States and Cities correctly. (linguistically - spelling.)

ie

Col B   |  Col C   |       Col D        |
-----------------------------------------
U.S.    | New York | Buffalo            |
U.S.    | New York | Manhattan; Buffalo |
India   | Karnataka| Bangalore          |

Also it validates the hierarchy correctly.

Dim nLastRow As Long
Dim nLastRowSheet2 As Long
Dim rngFnder As Range
Dim strFndAddress As String

Dim stString As String
Dim stArray() As String

'Get the last row
'Dim lastRow As Integer
nLastRow = Sheets("Sheet1").Cells(Rows.Count, 2).End(xlUp).Row
nLastRowSheet2 = Sheets("Jurisdictions").Cells(Rows.Count, 2).End(xlUp).Row

Dim c As Range
Dim d As Range
Dim e As Variant

'Turn screen updating off to speed up macro code.
'User won't be able to see what the macro is doing, but it will run faster.
Application.ScreenUpdating = False

For Each c In Worksheets("Sheet1").Range("D2:D" & nLastRow)
stString = c
stArray() = Split(stString, ";")
For Each e In stArray()
    e = Trim(e)

    strFndAddress = ""
    On Error Resume Next

        Set rngFnder = Sheets("Jurisdictions").Range("D2:D" & nLastRowSheet2).Find(e)

        If rngFnder Is Nothing And c <> "All" Then
            c.Interior.Color = vbRed

        Else
            strFndAddress = rngFnder.Address
            Do
                If c.Offset(, -1) = rngFnder.Offset(, -1) And c.Offset(, -2) = rngFnder.Offset(, -2) Then
                    strFndAddress = ""
                    Exit Do
                Else

                    Set rngFnder = Sheets("Jurisdictions").Range("D2:D" & nLastRowSheet2).FindNext(rngFnder)

                End If
            Loop While Not rngFnder Is Nothing And rngFnder.Address <> strFndAddress
        End If

        If rngFnder.Address = strFndAddress Then
            c.Interior.Color = vbRed
        End If
    On Error GoTo 0
    Set c = Nothing
    strFndAddress = ""
Next
Next

Now I have other requirements

  • There can be multiple states listed in 'state' cell separated only with semicolon.
  • If multiple states are listed under state; then the adjacent cell for City should( mandatorily) have value "All" only. (As if multiple states are listed then the above code can't validate the hierarchy correctly)
  • State cell may have value as "All" (ie All states; no state listed separately)
  • If state cell has value "All" then the adjacent city cell should ( mandatorily) have value "All" only.
  • There can be cases where a single state value is listed under State, but still adjacent city cell can have value "All"

To implement the above requirements I added following code

For Each c In Worksheets("Sheet1").Range("D2:D" & nLastRow)
        If c.Offset(, -1) = "All" And c = "All" Then
            c.Interior.Color = vbWhite
        End If
Next

and...

For Each c In Worksheets("Sheet1").Range("D2:D" & nLastRow)
    If c = "All" Then
    stString = c.Offset(, -1)
    stArray() = Split(stString, ";")
    End If
        For Each e In stArray()
        e = Trim(e)
        strFndAddress = ""

        On Error Resume Next

        Set rngFnder = Sheets("Jurisdictions").Range("C2:C" & nLastRowSheet2).Find(e)

        If rngFnder Is Nothing And e <> "All" Then
            'c.Interior.Color = c.Interior.Color (Do Nothing)
        Else
            strFndAddress = rngFnder.Address
            Do
            If c.Offset(, -2) = rngFnder.Offset(, -2) Then
                    strFndAddress = ""
                    c.Interior.Color = vbWhite
                    Exit Do
            Else
                    Set rngFnder = Sheets("Jurisdictions").Range("C2:C" & nLastRowSheet2).FindNext(rngFnder)

            End If
            Loop While Not rngFnder Is Nothing And rngFnder.Address <> strFndAddress
        End If

        If rngFnder.Address = strFndAddress Then
        'c.Interior.Color = c.Interior.Color (Do Nothing)
        End If
        On Error GoTo 0
        Set c = Nothing
        strFndAddress = ""

    Next
 Next

The last code is little erroneous.

If the 'State' cell has a single state and adjacent 'City' cell has value "All" :it validates it correctly.

But; If 'State' cell has multiple states separated with a semicolon; and adjacent 'City' cell has value "All" then code do not validate it correctly.

Can anyone help me where I am wrong? and also how I can increase the performance of code...

Looking at your code, it seems you are making this really really complicated, where it does not really have to be.

If I gather this correctly, users will be entering data in your excelworkbook and you want to make sure they do this correctly, following some rules. If they break the rules you will color the cell red.

The easier and faster way to do this is use the conditional formatting built in to excel. This will also allow you to split your code, and only check data that has actually changed: so no need to loop through all the cells.

Quick example:

  • If multiple states are listed under state; then the adjacent cell for City should( mandatorily) have value "All" only. (As if multiple states are listed then the above code can't validate the hierarchy correctly) You state that multiple states would be colon-separated. So you need a rule that states: if my cell under column B contains a ";" then my cell under column B must contain "All". If this is not so, I want to color the cell under column C red.

To add this rule you select the first cell of column C, and you choose "Conditional Formatting" from the Home tab in Excel;s ribbon. Then you create a new rule, and choose to use a formula to determine which cells to format. The formula you need is "=NOT(ISERROR(FIND(";";B1)))" Then you set the format to red Fill and save the rule.

This rule will now work for the first row, in order to make it work for the entire column, you can go to manage rules and adjust the "applies to" field to $C:$C or you can copy the formatting to the cells you want to use.

You can create a Conditional formatting rule for every rule you have on your workbook. You can also code functions for specific rules that you can not express easily in Excel's native funtions.

This will make your checks simple and easier to maintain, but it will also make your user feedback instantanious: as soon as they enter a value, the rules are applied.

First, let me state that the approach in my previous answer can easily be applied to a workbook containing data, and is the best way forward.

But as you ask for input on your code, maybe you should look at the second part of your else. If State has multiple states, and the state can be found on the jurisdictions tab, your code will go to the following else block:

strFndAddress = rngFnder.Address
Do
    If c.Offset(, -2) = rngFnder.Offset(, -2) Then
        strFndAddress = ""
        c.Interior.Color = vbWhite
        Exit Do
    Else
        Set rngFnder = Sheets("Jurisdictions").Range("C2:C" & nLastRowSheet2).FindNext(rngFnder)
    End If
Loop While Not rngFnder Is Nothing And rngFnder.Address <> strFndAddress

This block does nothing with the "All" value.

Also, this line confuses me (more than once):

c.Interior.Color = c.Interior.Color

Other tips you might enjoy:

  • rename your variables so you can actually read what they represent: "cityName" instead of "e" and cityCell instead of "c" makes a lot of difference.
  • Use the UsedRange of a worksheet to determine the last row and column. It is faster and more stable than using the End functions.
  • Do not use On Error Resume Next. This will surpress any errors and make it very hard to find any errors that occur.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM