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: enter image description here

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

+3


source to share


2 answers


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 constant vbLf

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

output of the code



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

    with vbLf

    . This gives an array of all individual lines.
  • The counter keeps track of the maximum number of rows added (actually rows-1

    , since these arrays 0-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 required Application.Transpose

    because it Split

    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 is 0-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.
+5


source


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!

+2


source







All Articles