0

The below code I have on an Excel sheet. I am using VBA to alter the contents of certain cells in order to format them correctly for my job. We keep a list of damages, bay locations, and VINs. Each one of these columns has their own certain formats, and for 2 of them I have working fine. Some of you may recognize some of this code from another post I had about getting the damage codes formatted correctly. The columns are ordered as such

Bay Location | VIN | Damage Code(s)

For VIN, all we do is upper-case the letters. Simple, got that done. The damage codes function works perfectly after I've altered it a bit to better suit my needs. Could not have done that without the original help I received here. Here's where things get weird, my boss, seeing that I had gotten this to work for the damage codes, asked me to get it to Auto-Format the bays. The bay locations at my job have a few possibilities, but there is always at least 1 letter in front such as

  1. H-5
  2. H-125
  3. HH-50
  4. 7A-70
  5. FNCE-13

In English, what I want done is this: Input unformatted bay, such as 7a12, uppercase the letters, split it by the numbers, and add a dash in between the two groups, and voila.

I had this working, even showed it to my boss. But then I added in the code to uppercase the VIN column and I started getting an error, highlighting the line

Set allMatches = RE1.Execute(strSource)

The RE1.test(strSource) runs fine, but trying to grab the matches/submatches now mysteriously throws an error. I originally used the text from This StackOverflow question to get it working. The error I get is something akin to it telling me that the object isn't set. I know the code is currently a mess, I had to leave mid-work (figured maybe something was wrong with my function, nope, same error when being ran directly from the original sub function).

Edit: The Error is as followed

Run Time Error '91' Object Variable or With block variable not set

And again, it highlights

allMatches = RE.Execute(str)

Any help is appreciated.

Private Sub Worksheet_Change(ByVal Target As Range)
    Dim KeyCells As Range
    Dim str As String, result As String
    Dim RE As Object
    Dim allMatches As Object

    ' The variable KeyCells contains the cells that will
    ' cause an alert when they are changed.
    Set KeyCells = Application.Union(Range("F3:F100"), Range("C3:C100"), Range("I3:I100"))
    Set RE = CreateObject("vbscript.regexp")

    If Not TypeName(Target.Value) = "Variant()" Then

        If Not Application.Intersect(KeyCells, Range(Target.Address)) _
            Is Nothing Then

            ' Display a message when one of the designated cells has been
            ' changed.
            ' Place your code here.
            str = ConvertString(Target)
            If (Not str = Target.Value And Not Target.Value = "") Then
                Target.Value = str
            End If

        End If

        ' Now we have to check the bays in order to auto format
        Set KeyCells = Application.Union(Range("A3:A100"), Range("D3:D100"), Range("G3:G100"))
        If Not Application.Intersect(KeyCells, Range(Target.Address)) _
            Is Nothing Then

            RE.Pattern = "([0-9]?[A-Z]{1,})\-?([0-9]{1,3})"
            RE.Global = True

            If Not Target.Value = "" And Not RE.test(Target.Value) Then
                    str = CStr(Target.Value)
                    RE.IgnoreCase = True
                    allMatches = RE.Execute(str)
                    MsgBox allMatches.Count

                    Target.Value = str
            End If

        End If

        Set KeyCells = Application.Union(Range("B3:B100"), Range("E3:E100"), Range("H3:H100"))

        If Not Application.Intersect(KeyCells, Range(Target.Address)) _
            Is Nothing Then

            RE.Pattern = "[a-z]?"
            RE.IgnoreCase = False

            If RE.test(Target.Value) Then
                Target.Value = UCase(Target.Value)
            End If

        End If
    End If
End Sub
Function FormatBay(str1 As Range) As String
    Dim result As String, strSource As String
    Dim allMatches As Object
    Dim RE1 As Object
    Set RE1 = CreateObject("vbscript.regexp")
    RE1.Pattern = "([0-9]?[A-Z]{1,})\-?([0-9]{1,3})"
    RE1.Global = True
    strSource = CStr(str1.Value)
    Set allMatches = RE1.Execute(strSource)
    result = "FF-12"
    If allMatches.Count <> 0 Then
        result = allMatches.Item(0)
    End If
    MsgBox result
    FormatBay = result
End Function
Function ConvertString(str1 As Range) As String
    Dim varStr As Variant
    Dim strSource As String, strResult As String
    Dim i As Integer

    For Each varStr In Split(Trim(str1.Value), " ")

            strSource = CStr(varStr)
        If InStr(strSource, ".") = 0 Then
            strResult = strResult & _
                Mid(strSource, 1, 2) & "." & _
                Mid(strSource, 3, 2) & "." & _
                Mid(strSource, 5, 1)
            If Len(strSource) > 5 Then
                strResult = strResult & "("
                For i = 6 To Len(strSource)
                    strResult = strResult & Mid(strSource, i, 1) & ","
                Next i
                strResult = Left(strResult, Len(strResult) - 1) & ")"
            End If
            strResult = strResult & " "
        Else
            strResult = strResult & strSource & " "
        End If
    Next

    If strResult = "" Then
        ConvertString = ""
    Else
        ConvertString = Left(strResult, Len(strResult) - 1)
    End If
End Function

EDIT : Here is what I got to work, I know it's sort of long and probably verbose, but I'm just learning VBA so when I learn a better way to do it, I will edit this post in the hopes of helping someone later.

Private Sub Worksheet_Change(ByVal Target As Range)
    Dim KeyCells As Range
    Dim str As String, result As String
    Dim RE As Object
    Dim allMatches As Object

    ' The variable KeyCells contains the cells that will
    ' cause an alert when they are changed.
    Set KeyCells = Application.Union(Range("F3:F100"), Range("C3:C100"), Range("I3:I100"))
    Set RE = CreateObject("vbscript.regexp")

    If Not TypeName(Target.Value) = "Variant()" Then


        ' Now we have to check the bays in order to auto format
        Set KeyCells = Application.Union(Range("A3:A100"), Range("D3:D100"), Range("G3:G100"))
        If Not Application.Intersect(KeyCells, Range(Target.Address)) _
            Is Nothing Then

            RE.Pattern = "([0-9]?[A-Z]{1,})\-?([0-9]{1,3})"
            RE.Global = True

            If Not Target.Value = "" And Not RE.test(Target.Value) Then
                    str = CStr(Target.Value)
                    str = FormatBay(str)

                    Target.Value = str
            End If

        End If

        Set KeyCells = Application.Union(Range("B3:B100"), Range("E3:E100"), Range("H3:H100"))

        If Not Application.Intersect(KeyCells, Range(Target.Address)) _
            Is Nothing Then

            RE.Pattern = "[a-z]?"
            RE.IgnoreCase = False

            If RE.test(Target.Value) Then
                Target.Value = UCase(Target.Value)
            End If

        End If

        Set KeyCells = Application.Union(Range("C3:C100"), Range("F3:F100"), Range("I3:I100"))

        If Not Application.Intersect(KeyCells, Range(Target.Address)) _
            Is Nothing Then

            ' Display a message when one of the designated cells has been
            ' changed.
            ' Place your code here.
            str = ConvertString(Target)
            If (Not str = Target.Value And Not Target.Value = "") Then
                Target.Value = str
            End If

        End If

    End If
End Sub
Function FormatBay(ByVal text As String) As String

    Dim result As String, bayLetter As String, bayNumber As String
    Dim length As Integer, i As Integer
    Dim allMatches As Object
    Dim RE As Object
    Set RE = CreateObject("vbscript.regexp")

    RE.Pattern = "([0-9]?[a-z]{1,})\-?([0-9]{1,3})"
    RE.Global = True
    RE.IgnoreCase = True

    Set allMatches = RE.Execute(text)

    If Not allMatches.Count = 0 Then
        bayLocation = allMatches.Item(0).submatches.Item(0)
        bayLocation = UCase(bayLocation)
        bayNumber = allMatches.Item(0).submatches.Item(1)
        length = Len(bayNumber)

        For i = 1 To (3 - length)
            bayNumber = "0" & bayNumber
        Next
        result = bayLocation & "-" & bayNumber
    End If

    FormatBay = result

End Function
Function ConvertString(str1 As Range) As String
    Dim varStr As Variant
    Dim strSource As String, strResult As String
    Dim i As Integer

    For Each varStr In Split(Trim(str1.Value), " ")

            strSource = CStr(varStr)
        If InStr(strSource, ".") = 0 And IsNumeric(strSource) Then
            strResult = strResult & _
                Mid(strSource, 1, 2) & "." & _
                Mid(strSource, 3, 2) & "." & _
                Mid(strSource, 5, 1)
            If Len(strSource) > 5 Then
                strResult = strResult & "("
                For i = 6 To Len(strSource)
                    strResult = strResult & Mid(strSource, i, 1) & ","
                Next i
                strResult = Left(strResult, Len(strResult) - 1) & ")"
            End If
            strResult = strResult & " "
        Else
            strResult = strResult & strSource & " "
        End If
    Next

    If strResult = "" Then
        ConvertString = ""
    Else
        ConvertString = Left(strResult, Len(strResult) - 1)
    End If
End Function
Community
  • 1
  • 1
Jhecht
  • 4,407
  • 1
  • 26
  • 44

2 Answers2

1

allMatches is an object (Type = MatchCollection) variable. You should use the Set keyword when assigning an object variable.

Set allMatches = RE.Execute(str)

As your code currently stands, I don't think it ever could have worked without raising this error, unless you or someone else had made an inadvertent edit to the code and changed the way this variable was assigned.

Hope this helps!

David Zemens
  • 53,033
  • 11
  • 81
  • 130
  • I had deleted it before and it had remained working (perhaps a cached version?) and then it just all of the sudden didn't. I tried adding that part back in but it didn't work. I worked through it managed to get it to work. – Jhecht Nov 04 '13 at 03:30
0

First, the regex [a-z]? is always going to match. If the first character in Target.Value happens to be a lowercase letter the regex will consume it. Otherwise it will match the empty string before the first character. You're testing for the presence of a lowercase letter, but the ? defeats the purpose by making the letter optional.

But I don't see why you need to do that test. You're going to change all letters to uppercase anyway, right? So just UCase the string and be done with it. Or wait until the conversion is done and UCase it then.

As for the actual conversion, your code is rather confusing, but I think you're doing a whole lot of unnecessary work. If you're processing strings like 7a12 individually, this should suffice:

RE.Pattern = "^([0-9]?[A-Z]{1,})-?([0-9]{1,3})$"
RE.IgnoreCase = True
result = UCase(RE.Replace(source, "$1-$2"))

Or am I missing something?

Alan Moore
  • 73,866
  • 12
  • 100
  • 156
  • No, I just didn't know about the replace function for the regexp object. Really new to VBA. – Jhecht Nov 04 '13 at 03:28