1

I am having several columns with text. Furthermore, I have a column, which is called Replacement text. This columns contains strings that have markers in it, like [1], [2], etc.

I would like to replace the markers with the text that is in the marker`s row.

enter image description here

For example, Here you can find [5] becomes Here you can find b, because [5] is the markers column and in the row of the string b is the value for the marker.

I was thinking of creating a large if-else construct and substitute the text, which is extremely error-prone.

However, I kindly ask you if there is an easier solution?

I appreciate your input!

Community
  • 1
  • 1
Carol.Kar
  • 4,581
  • 36
  • 131
  • 264
  • 2
    Yes, VBA could easily handle this. You do need to provide what you have tried so far. Remember that SO is not a code writing service but rather a forum to get pointed questions on coding issues. As a point to start with you can create a loop that runs through your range on column K, pulls the markers for each cell and substitutes for the appropriate string. Once you have your code, if you still have issues you can share the code on SO to get assistance on your issue. Regards, – nbayly Sep 15 '16 at 21:04

2 Answers2

3

This answer is highly plagiarised from Thomas Inzina's first version of his answer, but a very simple way of performing the replacement would be:

Sub ReplaceText()
    Dim r As Long
    Dim c As Long
    With ActiveSheet
        For r = 3 To .Range("K" & .Rows.Count).End(xlUp).Row
            For c = 1 To 10
                .Cells(r, "K").Replace "[" & c & "]", .Cells(r, c).Value
            Next
        Next
    End With
End Sub

The above code will attempt to do a substitution using all ten columns.

As Thomas has noted, the following code will only do the substitution if a substitution is necessary and therefore could be an order of magnitude faster, so is undoubtedly a better solution:

Sub ReplaceText()
    Dim r As Long
    Dim c As Long
    With ActiveSheet
        For r = 3 To .Range("K" & .Rows.Count).End(xlUp).Row
            For c = 1 To 10
                If Instr(.Cells(r, "K").Value, "[" & c & "]") > 0 Then
                    .Cells(r, "K").Replace "[" & c & "]", .Cells(r, c).Value
                End If
            Next
        Next
    End With
End Sub

(Many thanks to Thomas for his effort in performing speed tests on the two different methods.)

YowE3K
  • 23,852
  • 7
  • 26
  • 40
  • I ran a speed test it takes your original 78 seconds to process 100K records but only 18 seconds when you use Instr to avoid extra replacements. Apparently, Excel will write to the cell whether or not there are any changes. –  Sep 16 '16 at 06:17
  • Thanks @ThomasInzina - I will update the answer accordingly. – YowE3K Sep 16 '16 at 07:43
2

ReplaceBrackets1: uses RegEx to extract the column number. It takes 15.03 Seconds to processed 100K records.


Sub ReplaceBrackets1()
    'http://analystcave.com/excel-regex-tutorial/
    Dim c As Range
    Dim Match As Object, Matches As Object, regex As Object
    Set regex = CreateObject("VBScript.RegExp")

    With regex
        .Global = True
        .Pattern = "\[(.*?)\]"
    End With

    For Each c In Range("K3", Range("K" & Rows.Count).End(xlUp))
        If regex.Test(c.Text) Then
            Set Matches = regex.Execute(c.Text)
            For Each Match In Matches
                c.Replace Match, c.EntireRow.Columns(CInt(Match.SubMatches(0)))
            Next Match

        End If
    Next
End Sub

ReplaceBrackets2: loades the data into arrays, uses RegEx to extract the column number and only writes to the worksheet 1 time. It takes 1.27 seconds to process 100K records.


Sub ReplaceBrackets2()
'http://analystcave.com/excel-regex-tutorial/
    Dim x As Long, column As Long
    Dim arData, values
    Dim Match As Object, Matches As Object, regex As Object
    Set regex = CreateObject("VBScript.RegExp")

    With regex
        .Global = True
        .Pattern = "\[(.*?)\]"
    End With

    values = Range("K3", Range("K" & Rows.Count).End(xlUp))
    arData = Range("A3", "L" & UBound(values, 1) + 2)

    For x = 1 To UBound(values, 1)
        If regex.Test(values(x, 1)) Then
            Set Matches = regex.Execute(values(x, 1))
            For Each Match In Matches
                column = Match.SubMatches(0)
                values(x, 1) = arData(x, column)
            Next Match
        End If
    Next

    Range("K3", Range("K" & Rows.Count).End(xlUp)) = values

End Sub

After converting ReplaceBrackets1 into a UDF (getReplacedText) I was amazed to find that it only took 2.53 seconds to fill the formula in for a 100K records. I'm not sure way this would be faster that the original. But having that many formulas really slows down the spreadsheet.

getReplacedText: Uses a Static RegEx to parse the data.

Function getReplacedText(ReplacementText As String, Source As Range)
'http://analystcave.com/excel-regex-tutorial/
    Dim Match As Object, Matches As Object
    Static regex As Object
    If regex Is Nothing Then
        Set regex = CreateObject("VBScript.RegExp")

        With regex
            .Global = True
            .Pattern = "\[(.*?)\]"
        End With
    End If

    If regex.Test(ReplacementText) Then
        Set Matches = regex.Execute(ReplacementText)
        For Each Match In Matches
            ReplacementText = Replace(ReplacementText, Match, Source.Columns(CInt(Match.SubMatches(0))))
        Next Match

    End If

    getReplacedText = ReplacementText
End Function
  • You could potentially change `column = ...` to `For column = 1 To 10` and therefore multiple sets of brackets per cell would be handled. – YowE3K Sep 15 '16 at 21:38
  • Doing it this way I would have to change the starting point for each `Instr()`. Regex would be better or split. –  Sep 15 '16 at 21:48
  • Your Instr is only being used to work out the value of `column`, and wouldn't be needed if you looped across all possible values of `column`. (The guts of your solution is the `Cells(x, "k").Replace "[" & column & "]", Cells(x, column)`, which just needs to know the row number and a value of `column`.) – YowE3K Sep 15 '16 at 22:13
  • The problem is that I was using `Mid` to find the number between the first set of brackets. To find the second set of brackets I would have to trim the string or change the starting point of the `Instr`. In any case, I updated my answer to handle multiple sets. Let me know what you think. –  Sep 15 '16 at 22:23
  • I was just suggesting something like `For column = 1 To 10` `Cells(x, "K").Replace "[" & column & "]", Cells(x, column)` `Next`. That would loop across all 10 columns, replacing values as needed. – YowE3K Sep 15 '16 at 22:25
  • I see what you mean. If you added `If Instr(Cells(x, "K"),"[" & column & "]") Then`, I would accept your answer...lol. You should post it. The OP would probably prefer your approach. –  Sep 15 '16 at 22:28
  • I was thinking it would be faster to just do the replace, even if not required, than it would be to do the `Instr...` required to determine whether a replace was going to have any effect. – YowE3K Sep 15 '16 at 22:52
  • I treid to use your `UDF`, however, I am getting `Type mismatch` on line `Replace(ReplacementText, Match, Source.Columns(CInt(Match.SubMatches(0))))`. I tried to use `CStr` for using strings instead of integers, however, I still get an error. Any suggestions why I get this error? – Carol.Kar Sep 16 '16 at 14:11
  • 1
    Columns takes letters as string and numbers as numbers. Here is a sample formula `=getReplacedText(K5,A5:J5)`. What is your formula? –  Sep 16 '16 at 14:15
  • Thx for your reply! For values such as `[1] etc.` it works, but using `[test]`, I get back `#VALUE!`. I tried to use `CStr`, which gives me nothing back. Any suggestions? – Carol.Kar Sep 16 '16 at 14:26
  • 1
    That's quite different than your sample. It will require a reference to the column headers and either `WorksheetFunction.Match` or `Range().Find` to get the column number. I'm omw to work and probably won't be able to modify it till next week. –  Sep 16 '16 at 14:35