Replace text marker with column text - excel

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.
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!

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.)

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

Related

Counting the matching substrings in range

I am working on a workbook in which I need to count how many times the "St/" substring is present in a Range (Column Q). Note: I am interested in all the occurrences, not just the number of cells in which the substring is present.
Here is the code I am trying to work with (based on the comment of Santhosh Divakar - https://stackoverflow.com/a/23357807/12536295), but I receive a runtime error (13) when running it. What am I missing / doing wrong?
Dim lastrow, q as Integer
lastrow = Range("A1").End(xlToRight).End(xlDown).Row
With Application
q = .SumProduct((Len(Range("Q1:Q" & lastrow)) - Len(.Substitute(Range("Q1:Q" & lastrow), "St/", ""))) / Len("St/"))
End With
See if the code below helps you:
Public Sub TestCount()
lastrow = Range("Q" & Rows.Count).End(xlUp).Row
strformula = "=SUMPRODUCT(LEN(Q1:Q" & lastrow & ")-LEN(SUBSTITUTE(UPPER(Q1:Q" & lastrow & "),""/ST"","""")))/LEN(""/St"")"
MsgBox Evaluate(strformula)
End Sub
I think you can count the number of characters, replace your "St/" with nothing and then count the characters again and divide by len("St/"). Here's an example.
'''your existing code
Dim lCount As Long
Dim lCount_After As Long
'''set a Range to column Q
Set oRng = Range("Q1:Q" & lRow_last)
'''turn that range into a string
sValues = CStr(Join(Application.Transpose(oRng.Value2)))
lCount = Len(sValues)
lCount_After = lCount - Len(Replace(sValues, "St/", ""))
lCount_After = lCount_After / 3
Debug.Print lCount_After
Using ArrayToText() function
a) If you dispose of Excel version MS365 you can shorten a prior string building by evaluating the tabular ARRAYTOTEXT()
formula to get a joined string of all rows at once (complementing #Foxfire 's valid solution).
Note that it's necessary to insert the range address as string;
in order to fully qualify the range reference I use an additional External:=True argument.
b) VBA's Split() function eventually allows to return the number of found delimiters (e.g. "St/") via
UBound() function. It returns the upper boundary (i.e. the largest available subscript) for this
zero-based 1-dimensional split array.
Example: If there exist eight St/ delimiters, the split array consists
of nine elements; as it is zero-based the first element has index 0
and the last element gets identified by 8 which is already the wanted function result.
Function CountOccurrencies(rng As Range, Optional delim as String = "St/")
'a) get a final string (avoiding to join cells per row)
Dim txt As String
txt = Evaluate("ArrayToText(" & rng.Address(False, False, External:=True) & ")")
'b) get number of delimiters
CountOccurrencies = UBound(Split(txt, delim))
End Function
Not the cleanest one, but you can take all into arrays and split by St/. Size of that array would be how many coincidences you got:
Sub test()
Dim LR As Long
Dim MyText() As String
Dim i As Long
Dim q As Long
LR = Range("Q" & Rows.Count).End(xlUp).Row
ReDim Preserve MyText(1 To LR) As String
For i = 1 To LR Step 1
MyText(i) = Range("Q" & i).Value
Next i
q = UBound(Split(Join(MyText, ""), "St/"))
Debug.Print q
Erase MyText
End Sub
The output i get is 8
Please, note this code is case sensitive.
The TextJoin() function in Excel 2019+ is used:
Sub CalcSt()
Const WHAT = "St/": Dim joined As String
joined = WorksheetFunction.TextJoin("|", True, Columns("Q"))
Debug.Print (Len(joined) - Len(Replace(joined, WHAT, ""))) / Len(WHAT)
End Sub

VBA EXCEL Search for matches in a string of text in a column vs a different column on a different sheet. If match found, Copy & Paste

I'm trying desperately to learn Excel well enough to do this on my own but I can't figure this out. I really appreciate any help you can give me. I posted before with not nearly enough information, so this is the repost with more info.
A document is pasted in cell A9.
It fills every cell below it with lines of data, up to A200.
The lines of data look like this:
192800002001 19280 G RG474 56 DAY PMI COMPLETE
19280A001001 19280 G CB359 AN/PRC-152A 56 DAY PMI
19280A005001 19280 G CB360 AN/PRC-152A 56 DAY PMI
I need the program to search each cell in column A for the words that look like "RG474" or "CB359" and search in a reference table on a different sheet in the same book. The table on the reference table looks like this
RG474 | xxx474 0 | 0 | IN RACK | AF6
CB915 | xxx359 0 | 0 | IN RACK | AF6
For every match found it pastes the row from the reference table into the row of the match next to the pasted document (columns L-Q).
I've found some code online that I've tried to no avail, the two things I tried are here:
Dim lastRw1, lastRw2, nxtRw, m
'Determine last row with data, refrene
lastRw1 = Sheets("380 Refrence").Range("A" & Rows.Count).End(xlUp).Row
'Determine last row with data, Import
lastRw2 = Sheets("analyser").Range("A" & Rows.Count).End(xlUp).Row
'Loop through Import, Column A
For nxtRw = 9 To lastRw2
'Search Sheet1 Column C for value from Import
With Sheets("380 Refrence").Range("A9:A" & lastRw1)
Set m = .Find(Sheets("analyser").Range("A" & nxtRw), LookIn:=xlValues, lookat:=xlWhole)
'Copy Import row if match is found
If Not m Is Nothing Then
Sheets("analyser").Range("A" & nxtRw & ":F" & nxtRw).Copy _
Destination:=Sheets("380 Refrence").Range("L" & m.Row)
End If
End With
Next
End Sub
Sub CopyImportData()
Dim lastRw1, lastRw2, nxtRw, m
Dim code As String, RefRow As Integer
Dim rowValues
'Determine last row with data, 380 Refrencerene
lastRw1 = Sheets("380 Refrence").Range("A" & Rows.Count).End(xlUp).Row
'Determine last row with data, Import
lastRw2 = Sheets("analyser").Range("A" & Rows.Count).End(xlUp).Row
For Row = 9 To lastRw2
With Sheets("analyser").Cell(Row, 1)
'meet the laziest error handling ever to find your 380 Refrenceerence value
code = WorksheetFunction.Mid(.Value, WorksheetFunction.IfError(WorksheetFunction.IfError(WorksheetFunction.Search("CB??? ", .Value), WorksheetFunction.Search("RG??? ", .Value)), 1), 5)
End With
With Sheets("380 Refrence")
'Use Excel Match to find the 380 Refrenceerence row, which is offset by 8
'I swear I'll stop using iferror
380 RefrenceRow = WorksheetFunction.IfError(WorksheetFunction.Match(code, .Range("A9:A" & lastRw1), 0) + 8, -1)
'-1 is our safeword, copy the range
If RefRow <> -1 Then
.Range("A" & RefRow & ":F" & RefRow).Copy Destination:=Worksheets("analyser").Range("L" & Row)
End If
End With
Next Row
End Sub
I didn't write either of these and don't fully understand them, but I do get the Gist of it.
Here's a very trimmed down duplicate of the workbook: https://drive.google.com/open?id=1qCz8DUCz6tA5-KbxKDnvRq_KiBDkl4W5
This worked for me - I skipped some of the "find last cell" bits so you'll need to adjust for that
Sub Tester()
Dim c As Range, v, f
Dim ws380 As Worksheet, wsAn As Worksheet
Set ws380 = ThisWorkbook.Sheets("380 Reference")
Set wsAn = ThisWorkbook.Sheets("analyser")
For Each c In wsAn.Range("A1:A50") 'for example
If Len(c.Value) > 0 Then
v = GetMatch(c.Value)
Debug.Print c.Address, v
If Len(v) > 0 Then
'got a value - look it up...
Set f = ws380.Range("A9:A5000").Find(v, lookat:=xlWhole, _
lookin:=xlValues)
If Not f Is Nothing Then
f.Resize(1, 6).Copy c.EntireRow.Cells(1, "L") 'copy found row
End If
End If
End If
Next c
End Sub
Function GetMatch(txt As String)
Dim re As Object, allMatches, m
Set re = CreateObject("VBScript.RegExp")
'looking for two upper-case letters then 3 digits, or 3 letters plus 2 digits
' with a word boundary on each end
re.Pattern = "(\b([A-Z]{2}\d{3}\b)|(\b[A-Z]{3}\d{2})\b)"
re.ignorecase = False
re.Global = True
Set allMatches = re.Execute(txt)
For Each m In allMatches
GetMatch = m.Value
Exit For
Next m
End Function
Here's a good vbscript regexp reference:
https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/scripting-articles/ms974570(v=msdn.10)?redirectedfrom=MSDN

Split full name into lastname,firstname if lastname has multiple parts (i.e. van, de)

I have a list with multiple names, however, some names have multiple last names: Ex. "Eddie van Halen. I can't get my code to output: "van Halen, Eddie", instead it outputs: "Van, Eddie"
Would there be a way to check if names() has more than 2 parts, and if so to include names(1) & names(2) as last name instead of checking for "van". That is if a last name includes other parts such as "de".
Additionaly, if the full name does not have multiple parts, ex: "volunteer", the code should skip this name.
Here is my current code:
Sub Filter()
Dim r As Range
Dim c As Range
Dim names() As String
Dim lastrow As Long
lastrow = ActiveSheet.Cells(Rows.Count, 2).End(xlUp).Row
With ActiveSheet
Set r = .Range("K3:K" & lastrow)
For Each c In r
names = Split(c.Value, " ")
If IsEmpty(c.Value) Then Exit Sub
ElseIf InStr(c.Value, "van") > 0 Then
c.Value = names(1) & names(2) & ", " & names(0)
Else
c.Value = names(1) & ", " & names(0)
End If
Next c
End With
End Sub
Split takes a third argument, "Limit", which can be used to stop splitting once the result will have that many entries.
names = Split(c.Value, " ", 2)
In this case names will always have at most 2 elements.
As an option, you could use regular expression. The following regular expression matches all words which do not begin with van, von, de etc. You can add your words at will. As an edge case, the name itself can start with van or von (for instance, Vonder). In order to handle this case, I have added \b into match of the exclusion, so these prefixes must be stand-alone. The other case, as #ScottCraner noted, is three parts names (like Mary Lou Smith in his example). In this case you can maneuver these cases with Count of matches (x variable). For instance, you can concatenate any parts, if there are three names.
Sub F()
'// Tools -> References -> Microsoft VBSscript Regular Expressions 5.5
Dim re As RegExp, mc As MatchCollection, m As Match, s$, x%
Set re = New RegExp
re.IgnoreCase = True
re.Global = True
re.Pattern = "\b(?!(van|von|de)\b)[a-z]+"
Set mc = re.Execute("van Halen, Vanzen")
If mc.Count > 0 Then
For x = 0 To mc.Count - 1
MsgBox mc(x)
Next
Else
MsgBox "No matches were found.", vbExclamation
End If
'// Output:
'// Halen
'// Vanzen
End Sub

Excel Field Parse Hel

We work with very complicated filenames for the data we work on. I will X out the parts of the file name that aren't relevant. What makes this tricky is that some "pieces" of the file name can vary in length and sometimes possess operator error.
XXXX_M222.222_080816_015457MST_XXXX_XXXX.XX
Now I will break these three fields down for better understanding:
M222.2222 will always start with an M followed by a variable string of numbers to annotate the frequency in MHz.
080816 is just the date, will always look like this
015457MST is the time we took the survey, or when the "ping" hits. It can vary in regards to the seconds will be dropped off. 01 (hour) 54 (min) 57 (sec). This will always end in MST.
I would like to take these three fields and parse them into three separate fields in Excel.
I got bored and decided to write it for you. This will handle all in a specified range. It removes the M and the MST and the end of each line and moves the broken down sub strings off net to the original line.
Feel free to upvote and mark as answered when youre satisfied with the end result
edit
if you got bored you could actually figure out a way to actually make the date and time stamp into a usuable format instead of the god awful output thats its in now.
Private Sub derpy()
Dim vAr As String
Dim vAr2 As String
Dim rArray() As String
Dim rNg As Range
Dim rCell As Range
Dim count As Integer
Set rNg = ThisWorkbook.Sheets("Sheet1").Range("A1:A7")
count = 1
For Each rCell In rNg.Cells
vAr = rCell
vAr2 = Mid(vAr, InStr(vAr, "M") + 1, InStr(vAr, "MST") - InStr(vAr, "M") - 1)
rArray = Split(vAr2, "_")
newDate = newDate & Left(rArray(1), 2) & "/"
newDate = newDate & Mid(rArray(1), 3, 2) & "/"
newDate = newDate & Mid(rArray(1), 5, 2)
newDate = Format(newDate, "mm-dd-yyyy")
newDate = newDate & "-" & Left(rArray(2), 2) & "."
newDate = newDate & Mid(rArray(2), 3, 2) & "."
newDate = newDate & Mid(rArray(2), 5, 2)
newDate = Format(newDate, "mm-dd-yyyy hh-mm-ss")
rCell.Offset(0, 1).Value = newDate
newDate = ""
Next rCell
End Sub
Here's a nice quick way of parsing a string that has a common delimiter in excel. You can feed in your file name and it will pick out the three parts you want without any ugly loops and if statements.
Sub parseFileName(filename As String)
Dim freq As String
Dim fDate As String
Dim fTime As String
freq = Split(filename, "_")(1)
fDate = Split(filename, "_")(2)
fTime = Split(filename, "_")(3)
Debug.Print freq, fDate, fTime
End Sub
Called from the immediate window:
parsefileName "XXXX_M222.222_080816_015457MST_XXXX_XXXX.XX"
M222.222 080816 015457MST
So... the magic is really in the SPLIT() function which takes in a string and a delimiter and "splits" the string into an array. Instead of storing the array in an array variable, we just treat the SPLIT() function itself as the array and use the values after the Split() function (1), (2), and (3) which is the 2nd, 3rd, and 4th tokens in the string to pull the values through. It's super quick and it doesn't matter how long each token is since we are delimiting by a character.
Editing to add an example of Split() while iterating over a column of data:
If you have a list of these filenames in Column A (Rows 1 through 5) of Sheet1, you could write out these three tokens into Column B, C, and D using the following.
Sub parseFileNames(filename As String)
'Declare a range object to hold the cell that we are splitting
Dim rngCell As Range
For Each rngCell In Sheet.Range("A1:A5").Cells
'Using offset here to move over 1, 2, and 3 columns, respectively
' from the cell that we are splitting.
rngCell.Offset(0, 1).Value = Split(rngCell.Value, "_")(1)
rngCell.Offset(0, 2).Value = Split(rngCell.Value, "_")(2)
rngCell.Offset(0, 3).Value = Split(rngCell.Value, "_")(3)
Next rngCell
End Sub

Convert cell content to new format

My workplace is changing CMS systems and we have around 5,000 products to import. The problem comes with image URL formatting as the two systems are laid out vastly different. I need a function or VB code to convert one cell:
Main|1|Vaterra/VTR03014C-1.jpg;VTR03014C|2|Vaterra/VTR03014C-2.jpg;VTR03014C|3|Vaterra/VTR03014C-3.jpg;VTR03014C|4|Vaterra/VTR03014C-4.jpg;VTR03014C|5|Vaterra/VTR03014C-5.jpg;VTR03014C|6|Vaterra/VTR03014C-6.jpg;VTR03014C|7|Vaterra/VTR03014C-7.jpg;VTR03014C|8|Vaterra/VTR03014C-8.jpg;VTR03014C|9|Vaterra/VTR03014C-9.jpg;VTR03014C|10|Vaterra/VTR03014C-10.jpg;VTR03014C|11|Vaterra/VTR03014C-11.jpg;VTR03014C|12|Vaterra/VTR03014C-12.jpg;VTR03014C|13|Vaterra/VTR03014C-13.jpg;VTR03014C|14|Vaterra/VTR03014C-14.jpg
into two cells containing:
Vaterra/VTR03014C-1.jpg
and this is where it gets tricky:
Vaterra/VTR03014C-2.jpg;Vaterra/VTR03014C-3.jpg;Vaterra/VTR03014C-4.jpg;Vaterra/VTR03014C-5.jpg;Vaterra/VTR03014C-6.jpg;Vaterra/VTR03014C-7.jpg;Vaterra/VTR03014C-8.jpg;Vaterra/VTR03014C-9.jpg;Vaterra/VTR03014C-10.jpg;|Vaterra/VTR03014C-11.jpg;Vaterra/VTR03014C-12.jpg;Vaterra/VTR03014C-13.jpg;Vaterra/VTR03014C-14.jpg
Notice how the "Main|1|" has been removed also, the tricky part is that not all of these begin with or contain "Main|1|" and not all of the options begin with or contain "Vaterra".
The main steps would be to remove each image's suffixes and then capture the line of text up to ".jpg" and move it to a separate cell.
As you have VBA tag, here is a quickest VBA approach.
Assuming your your data is in column A starting from row 1 on sheet1.
This macro will write the below two lines in column B and C respectively.
Column B
Vaterra/VTR03014C-1.jpg
Column C
Vaterra/VTR03014C-2.jpg;Vaterra/VTR03014C-3.jpg;Vaterra/VTR03014C-4.jpg;Vaterra/VTR03014C-5.jpg;Vaterra/VTR03014C-6.jpg;Vaterra/VTR03014C-7.jpg;Vaterra/VTR03014C-8.jpg;Vaterra/VTR03014C-9.jpg;Vaterra/VTR03014C-10.jpg;|Vaterra/VTR03014C-11.jpg;Vaterra/VTR03014C-12.jpg;Vaterra/VTR03014C-13.jpg;Vaterra/VTR03014C-14.jpg
Here is the macro.
Public RegMatchArray
Sub test()
Dim sh As Worksheet
Dim rowCount As Long
Dim i, j As Integer
Dim strValue, strValue1, strValue2 As String
Set sh = Sheets("Sheet1")
rowCount = sh.Range("A1048576").End(xlUp).Row
For i = 1 To rowCount
strValue = sh.Cells(i, 1).Value
If InStr(1, strValue, "Main|1|") > 0 Then
strValue = Replace(strValue, "Main|1|", "")
End If
iPos = InStr(1, strValue, ";")
strValue1 = Left(strValue, iPos - 1)
strValue2 = Mid(strValue, iPos + 1, Len(strValue) - iPos - 1)
Call splitUpRegexPattern(strValue2, "([\w\s-]+?)\/([\w\s-]+?\.jpg)")
For j = LBound(RegMatchArray) To UBound(RegMatchArray)
If j < 1 Then
strValue2 = RegMatchArray(j)
Else
strValue2 = strValue2 & ";" & RegMatchArray(j)
End If
Next
sh.Cells(i, 2).Value = strValue1
sh.Cells(i, 3).Value = strValue2
Next
Set sh = Nothing
End Sub
Public Function splitUpRegexPattern(targetString, strPattern)
Dim regEx As New RegExp
Dim strReplace As String
Dim arrArray()
i = 0
'CREATE THE REGULAR EXPRESSION
regEx.Pattern = strPattern
regEx.IgnoreCase = True
regEx.Global = True
'PERFORM THE SEARCH
Set Matches = regEx.Execute(targetString)
'REPORTING THE MATCHES COLLECTION
If Matches.Count = 0 Then
RegMatchArray = ""
Else
'ITERATE THROUGH THE MATCHES COLLECTION
For Each Match In Matches
'ADD TO ARRAY
ReDim Preserve arrArray(i)
arrArray(i) = Match.Value
i = i + 1
Next
RegMatchArray = arrArray
RegExpMultiSearch = 0
End If
If IsObject(regEx) Then
Set regEx = Nothing
End If
If IsObject(Matches) Then
Set Matches = Nothing
End If
End Function
Note: You have to add "Microsoft VBSript Regular Expressions 5.5" reference by going into Tools -> References.
If you don't want to keep the original column A, change the below lines. This will delete the original data and give you the result in column A and B.
From:
sh.Cells(i, 2).Value = strValue1
sh.Cells(i, 3).Value = strValue2
To:
sh.Cells(i, 1).Value = strValue1
sh.Cells(i, 2).Value = strValue2
With some tweeks, you will be able to make it happen without VBA.
First, replace | and / with ; so that you can have a consistent delimiter.
Also, you can remove Main|1| by replacing it with empty space.
Now, choose Data => Text to Columns
Choose the option Delimeted
you can now use the delimeter semicolon and you will have data in separate cells with the as in each cell.
You can now remove unwanted entries.
As an alternate, here is a formula solution. Assuming the large single block of text is in cell A1, put this formula in cell B1 and copy down until it starts giving you errors:
=TRIM(MID(SUBSTITUTE("|"&$A$1,";",REPT(" ",LEN($A$1))),LEN($A$1)*(ROW(A1)-1)+1+LOOKUP(2,1/(MID(SUBSTITUTE("|"&$A$1,";",REPT(" ",LEN($A$1))),LEN($A$1)*(ROW(A1)-1)+ROW(INDIRECT("1:"&LEN($A$1))),1)="|"),ROW(INDIRECT("1:"&LEN($A$1)))),LEN($A$1)))
The errors mean that there are no more entries to return, so you can delete the cells with errors, and then select all the cells with the formula -> Copy -> Right-click -> Paste Special -> Values to convert them to just be text instead of formulas. (I highly recommend doing that because the Indirect function is volatile and can greatly slow down your workbook if you have many formula cells with it.)

Resources