Split lines that have multiline text and single line text.
I am trying to figure out how to split rows of data where columns B, C, D in a row contain multiple rows and others do not. I figured out how to split multi-line cells if I copy only those columns to a new sheet, manually insert the rows and then run the macro below (this is only for column A), but I get lost in coding rest.
This is what the data looks like:
So, for row 2, I need it to split into 6 rows (one for each row in cell B2) with the text in cell A2 in A2: A8. I also need columns C and D, separated in the same way as B, and then columns E: CP is the same as column A.
Here is the code to split the cells in columns B, C, D:
Dim iPtr As Integer
Dim iBreak As Integer
Dim myVar As Integer
Dim strTemp As String
Dim iRow As Integer
iRow = 0
For iPtr = 1 To Cells(Rows.Count, col).End(xlUp).Row
strTemp = Cells(iPtr1, 1)
iBreak = InStr(strTemp, vbLf)
Range("C1").Value = iBreak
Do Until iBreak = 0
If Len(Trim(Left(strTemp, iBreak - 1))) > 0 Then
iRow = iRow + 1
Cells(iRow, 2) = Left(strTemp, iBreak - 1)
End If
strTemp = Mid(strTemp, iBreak + 1)
iBreak = InStr(strTemp, vbLf)
Loop
If Len(Trim(strTemp)) > 0 Then
iRow = iRow + 1
Cells(iRow, 2) = strTemp
End If
Next iPtr
End Sub
Here is a link to the example file (note that this file has 4 lines, the actual sheet has over 600): https://www.dropbox.com/s/46j9ks9q43gwzo4/Example%20Data.xlsx?dl=0
source to share
This is a pretty interesting question and something I saw earlier. I went ahead and wrote a general solution for it, as it seems like a useful bit of code to myself.
There are almost only two assumptions in the data:
- Returns are represented by
Chr(10)
either a constantvbLf
. - Data belonging to a lower row has enough returns to line up. This is similar to your case, as there are characters returned that appear to be lined up the way you want.
Output images enlarged to show all data for A:D
. Note that the code below processes all columns by default and outputs to a new sheet . You can limit the columns if you like, but it was too tempting to make it generic.
code
Sub SplitByRowsAndFillBlanks()
'process the whole sheet, could be
'Intersect(Range("B:D"), ActiveSheet.UsedRange)
'if you just want those columns
Dim rng_all_data As Range
Set rng_all_data = Range("A1").CurrentRegion
Dim int_row As Integer
int_row = 0
'create new sheet for output
Dim sht_out As Worksheet
Set sht_out = Worksheets.Add
Dim rng_row As Range
For Each rng_row In rng_all_data.Rows
Dim int_col As Integer
int_col = 0
Dim int_max_splits As Integer
int_max_splits = 0
Dim rng_col As Range
For Each rng_col In rng_row.Columns
'splits for current column
Dim col_parts As Variant
col_parts = Split(rng_col, vbLf)
'check if new max row count
If UBound(col_parts) > int_max_splits Then
int_max_splits = UBound(col_parts)
End If
'fill the data into the new sheet, tranpose row array to columns
sht_out.Range("A1").Offset(int_row, int_col).Resize(UBound(col_parts) + 1) = Application.Transpose(col_parts)
int_col = int_col + 1
Next
'max sure new rows added for total length
int_row = int_row + int_max_splits + 1
Next
'go through all blank cells and fill with value from above
Dim rng_blank As Range
For Each rng_blank In sht_out.Cells.SpecialCells(xlCellTypeBlanks)
rng_blank = rng_blank.End(xlUp)
Next
End Sub
How it works
There are comments in the code to highlight what's going on. Here's a high-level overview:
- In general, we iterate over each row of data, processing each column individually.
- The text of the current cell
Split
withvbLf
. This gives an array of all individual lines. - The counter keeps track of the maximum number of rows added (actually
rows-1
, since these arrays0-indexed
. - Now the data can be displayed on a new sheet. This is easy because we can just dump the array we created for
Split
. The only tricky part is getting to the right place on the sheet. To do this, there is a counter for the current column offset and a global counter to determine how many common rows should be offset.Offset
moves us to the right cell;Resize
ensures that all lines are output. Finally, it is requiredApplication.Transpose
because itSplit
returns an array of strings and we are dumping the column. - Update counters. The column offset is incremented each time. The line offset is updated to add enough lines to cover the last maximum (
+1
since this is0-indexed
) - Finally, I end up using my waterfall bay (your previous question) in all of the spaces that were created to ensure there are no spaces. I give up on error checking because I am assuming there are spaces.
source to share
Thanks for providing a sample. This task was so interesting that I thought about writing some code to do it. You can more than customize it to your satisfaction, and I hope your team can use RDBMS to manage such data in the future.
Sub OrganizeSheet()
Dim LastRow As Integer
LastRow = GetLastRow()
Dim Barray() As String
Dim Carray() As String
Dim Darray() As String
Dim LongestArray As Integer
Dim TempInt As Integer
Dim i As Integer
i = 1
Do While i <= LastRow
Barray = Split(Range("B" & i), Chr(10))
Carray = Split(Range("C" & i), Chr(10))
Darray = Split(Range("D" & i), Chr(10))
LongestArray = GetLongestArray(Barray, Carray, Darray)
If LongestArray > 0 Then
' reset the values of B, C and D columns
On Error Resume Next
Range("B" & i).Value = Barray(0)
Range("C" & i).Value = Carray(0)
Range("D" & i).Value = Darray(0)
Err.Clear
On Error GoTo 0
' duplicate the row multiple times
For TempInt = 1 To LongestArray
Rows(i & ":" & i).Select
Selection.Copy
Range(i + TempInt & ":" & i + TempInt).Select
Selection.Insert Shift:=xlDown
' as each row is copied, change the values of B, C and D columns
On Error Resume Next
Range("B" & i + TempInt).Value = Barray(TempInt)
If Err.Number > 0 Then Range("B" & i + TempInt).Value = ""
Err.Clear
Range("C" & i + TempInt).Value = Carray(TempInt)
If Err.Number > 0 Then Range("C" & i + TempInt).Value = ""
Err.Clear
Range("D" & i + TempInt).Value = Darray(TempInt)
If Err.Number > 0 Then Range("D" & i + TempInt).Value = ""
Err.Clear
On Error GoTo 0
Application.CutCopyMode = False
Next TempInt
' increment the outer FOR loop counters
LastRow = LastRow + LongestArray
i = i + LongestArray
End If
i = i + 1
Loop
End Sub
' ----------------------------------
Function GetLongestArray(ByRef Barray() As String, ByRef Carray() As String, ByRef Darray() As String)
GetLongestArray = UBound(Barray)
If UBound(Carray) > GetLongestArray Then GetLongestArray = UBound(Carray)
If UBound(Darray) > GetLongestArray Then GetLongestArray = UBound(Darray)
End Function
' ----------------------------------
Function GetLastRow() As Integer
Worksheets(1).Select
Range("A1").Select
Selection.End(xlDown).Select
GetLastRow = Selection.Row
Range("A1").Select
End Function
Take a picture!
source to share