View Full Version : [SOLVED:] Unmerge Cells & Copy Cells Down to Next Line of Data
t0mato
01-05-2022, 10:11 AM
Hello!
I have a (what I believe to be) fairly simple request I am looking to achieve with Excel VBA.
Please see attached report. 
I am simply looking to unmerge each grouped cell in column D (Org Level 3) and copy the first line down to the next line. See images below:
Current State:
29283  
Goal State:
29284
Simply put, the macro/VBA code should unmerge the data in column D and copy the first line down to the last line until the data changes, and loop for each area to the bottom of the data. Additionally, you will notice in line 1227 where column A is "Warehouse" - the code should disregard this section since the column D is not grouped. I.e., stop where the column A line = "warehouse".
I am planning to macro record the unmerge of the first group starting at the top and try to have it loop until it reaches warehouse then stop but am not exactly sure how to write this.
Thanks so much for the help!
Paul_Hossler
01-05-2022, 12:12 PM
Try this
Option Explicit
Sub UnmergeAndFill()
    Dim rowLast As Long, i As Long
    Dim aryAreas() As Range
    Dim cntAreas As Long
    
    With Worksheets("HIN")
        rowLast = .Cells(.Rows.Count, 4).End(xlUp).Row
        i = 2
        Do While i <= rowLast
            If .Cells(i, 4).MergeCells Then
                cntAreas = cntAreas + 1
                ReDim Preserve aryAreas(1 To cntAreas)
                Set aryAreas(cntAreas) = .Cells(i, 4).MergeArea
            End If
            
            i = .Cells(i, 4).End(xlDown).Row
        Loop
        For i = LBound(aryAreas) To UBound(aryAreas)
            aryAreas(i).UnMerge
            aryAreas(i).Value = aryAreas(i).Cells(1, 1).Value
        Next i
    End With
    MsgBox "Done"
End Sub
t0mato
01-05-2022, 12:23 PM
This works perfectly, thanks so much!
t0mato
01-20-2022, 12:20 PM
How would I modify this to iterate through all sheets in the workbook (there are 25), except for the first sheet (which is an instruction sheet). 
I've tried the following (not sure how to exclude the first sheet so I figured i'd just have it run anyway, through the sheet doesnt have any numbers) but this doesn't work, it doesn't even return an error:
Public Sub IterateSheets()Dim S As Integer
S = 1
Do While S = Worksheets.Count
Worksheets(S).Select
UnmergeAndFill
S = S + 1
Loop
End Sub
Public Sub UnmergeAndFill()
' Unmerge and Fill
Dim rowLast As Long, i As Long
Dim aryAreas() As Range
Dim cntAreas As Long
rowLast = .Cells(.Rows.Count, 4).End(xlUp).Row
i = 2
Do While i <= rowLast
If .Cells(i, 4).MergeCells Then
cntAreas = cntAreas + 1
ReDim Preserve aryAreas(1 To cntAreas)
Set aryAreas(cntAreas) = .Cells(i, 4).MergeArea
End If
i = .Cells(i, 4).End(xlDown).Row
Loop
For i = LBound(aryAreas) To UBound(aryAreas)
aryAreas(i).UnMerge
aryAreas(i).Value = aryAreas(i).Cells(1, 1).Value
Next i
End With
' Unmerge and fill
End Sub
Paul_Hossler
01-20-2022, 01:37 PM
Option Explicit
Sub UnmergeAndFill()
    Dim rowLast As Long, i As Long
    Dim aryAreas() As Range
    Dim cntAreas As Long
    Dim ws As Worksheet
    
    For Each ws In ThisWorkbook.Worksheets
        With ws
            If .Name = "Instructions" Then GoTo NextSheet
        
            Erase aryAreas
            ReDim aryAreas(1 To 1)
            cntAreas = 0
        
            rowLast = .Cells(.Rows.Count, 4).End(xlUp).Row
    
            i = 2
            Do While i <= rowLast
                If .Cells(i, 4).MergeCells Then
                    cntAreas = cntAreas + 1
                    ReDim Preserve aryAreas(1 To cntAreas)
                    Set aryAreas(cntAreas) = .Cells(i, 4).MergeArea
                End If
                
                i = .Cells(i, 4).End(xlDown).Row
            Loop
    
    
            For i = LBound(aryAreas) To UBound(aryAreas)
                aryAreas(i).UnMerge
                aryAreas(i).Value = aryAreas(i).Cells(1, 1).Value
            Next i
        End With
NextSheet:
    Next
    MsgBox "Done"
End Sub
t0mato
01-20-2022, 05:43 PM
Hello! Thanks for the help. This returns an error, however. 
29340
29341
Any ideas on this?
Paul_Hossler
01-20-2022, 06:17 PM
It works OK when the worksheets have the proper format (see attachment), so I'm guessing that a worksheet is formatted differently
Add the marked line and see which worksheet caused the issue and maybe attach a workbook with just the problematic worksheet
        With ws
        
            MsgBox .Name    '  <<<<<<<<<<<<<<<<<<<<<<<
        
            If .Name = "Instructions" Then GoTo NextSheet
t0mato
01-21-2022, 07:09 AM
I've attached the updated workbook (with multiple sheets) here.
The code below works perfectly, I just need it to iterate through all sheets (excluding "Instructions") in the workbook (there are 25, I just included a few).
Public Sub UnmergeAndFill()
Dim rowLast As Long, i As Long
Dim aryAreas() As Range
Dim cntAreas As Long
    
    With Worksheets("Raw Data")
        rowLast = .Cells(.Rows.Count, 4).End(xlUp).Row
        i = 2
        
        Do While i <= rowLast
            If .Cells(i, 4).MergeCells Then
                cntAreas = cntAreas + 1
                ReDim Preserve aryAreas(1 To cntAreas)
                Set aryAreas(cntAreas) = .Cells(i, 4).MergeArea
            End If
            
            i = .Cells(i, 4).End(xlDown).Row
        Loop
        For i = LBound(aryAreas) To UBound(aryAreas)
            aryAreas(i).UnMerge
            aryAreas(i).Value = aryAreas(i).Cells(1, 1).Value
        Next i
    End With
End Sub
Thanks again for the help!
georgiboy
01-21-2022, 07:32 AM
That should be:
Public Sub UnmergeAndFill()    Dim rowLast As Long, i As Long
    Dim aryAreas() As Range
    Dim cntAreas As Long
    Dim ws As Worksheet
    
    For Each ws In ThisWorkbook.Worksheets
        With ws
            If .Name <> "Instructions" Then
                rowLast = .Cells(.Rows.Count, 4).End(xlUp).Row
                i = 2
                Do While i <= rowLast
                    If .Cells(i, 4).MergeCells Then
                        cntAreas = cntAreas + 1
                        ReDim Preserve aryAreas(1 To cntAreas)
                        Set aryAreas(cntAreas) = .Cells(i, 4).MergeArea
                    End If
                    i = .Cells(i, 4).End(xlDown).Row
                Loop
                For i = LBound(aryAreas) To UBound(aryAreas)
                    aryAreas(i).UnMerge
                    aryAreas(i).Value = aryAreas(i).Cells(1, 1).Value
                Next i
            End If
        End With
    Next ws
End Sub
Paul_Hossler
01-21-2022, 09:28 AM
I've attached the updated workbook (with multiple sheets) here.
The code below works perfectly, I just need it to iterate through all sheets (excluding "Instructions") in the workbook (there are 25, I just included a few).
Thanks again for the help!
I believe that the macro from my Post#5 already does what you want.
The 'For Each' loops through all WS
The If .Name ... skips 'Instructions'
    For Each ws In ThisWorkbook.Worksheets
        With ws
        
            If .Name = "Instructions" Then GoTo NextSheet
Using the sample XLSX from your post #8, it ran to completion without error
Since you changed the above to a specific worksheet, it would not do all sheets
    With Worksheets("Raw Data")
georgiboy
01-21-2022, 09:32 AM
Sorry Paul, missed that...
Paul_Hossler
01-21-2022, 09:37 AM
:thumb
t0mato
01-21-2022, 12:48 PM
You're right! It does work.. I was just adding the code to the wrong module (sorry I'm new to this). I made it a Public code and saved it to my PERSONAL project. When running from there, I get the error mentioned above. I thought in making it Public I could run it in any workbook? In other words, I get the error when I add the macro/module to my PERSONAL project. When I add it to the currently opened workbook, it works fine.
Paul_Hossler
01-21-2022, 02:53 PM
Then use
 ... In ActiveWorkbook.Worksheets
and not
 ... In ThisWorkbook.Worksheets
Excel can only do what you tell it to do :devil2:
t0mato
01-21-2022, 03:19 PM
Wow. I am still very much a VBA novice (obviously) with only one course under my belt. Still a lot to learn clearly. 
If I want to apply filters to each sheet, I think I would need to add the autofilter clause somewhere within the loop. Any ideas on where this should be added? Is it Selection.Autofilter? Will it need to be in a separate loop?
Paul_Hossler
01-21-2022, 07:06 PM
If I want to apply filters to each sheet, I think I would need to add the autofilter clause somewhere within the loop.
Probably
That could be a little tricky
More details and an example would be helpful
t0mato
01-24-2022, 08:00 AM
Thanks again for all of the help. I'd just like to add the filter buttons (Ctrl + shift + L) to each sheet. So once it does the unmerge & fill, just apply filter buttons to the columns. Let me know if further information is needed!
Paul_Hossler
01-24-2022, 04:33 PM
I don't have any unmodified data to test with, but I think just adding the last line would do it
            For i = LBound(aryAreas) To UBound(aryAreas)                aryAreas(i).UnMerge
                aryAreas(i).Value = aryAreas(i).Cells(1, 1).Value
            Next i
            
            If Not .AutoFilterMode Then .Rows(1).AutoFilter
t0mato
03-20-2022, 01:04 PM
This works perfectly. Thanks for all of the help! 
I do have one final question with this. If I wanted to apply the same process to different columns, where is the code would this be adjusted? Right now it is doing the merge & unfill on column C. I now need it to apply to columns A-F. I've done my best to find where it defines to apply to column C so that I may adjust accordingly but cannot figure it out.
Any help here would be greatly appreciated!
Paul_Hossler
03-20-2022, 02:42 PM
Like this?
Option Explicit
Sub UnmergeAndFill()
    Dim r As Long, c As Long
    Dim rData As Range
    Dim ws As Worksheet
    
    For Each ws In ActiveWorkbook.Worksheets
        If ws.Name = "Instructions" Then GoTo NextSheet
        
        ws.Range("A:F").UnMerge
        
        Set rData = ws.Cells(1, 1).CurrentRegion
                
        With rData
            For r = 3 To .Rows.Count
                For c = 1 To 6
                    If Len(.Cells(r, c).Value) = 0 Then .Cells(r, c).Value = .Cells(r - 1, c).Value
                Next c
            Next r
        End With
            
        If Not ws.AutoFilterMode Then ws.Rows(1).AutoFilter
NextSheet:
    Next
    MsgBox "Done"
End Sub
t0mato
03-20-2022, 05:58 PM
Paul, that is exactly it. Just trying to wrap my head around the change here.
Thanks again!
I used the file in your first post:
This is all you need for the first 4 columns:
Sub M_snb()
   Sheet1.Cells.UnMerge
   For Each it In sheet1.Columns(1).Resize(, 4).SpecialCells(4).Areas
      it.Value = it.Offset(-1).Cells(1).Resize(, it.Columns.Count).Value
   Next
End Sub
For the first 4 columns in all sheets in the active workbook:
Sub M_snb()
   For Each sh In Sheets
    sh.Cells.UnMerge
    
    If sh.Name <> "Instructions" Then
        For Each it In sh.Columns(1).Resize(, 4).SpecialCells(4).Areas
           it.Value = it.Offset(-1).Cells(1).Resize(, it.Columns.Count).Value
        Next
    End If
  Next
End Sub
NB. You should never use merged cells.
Paul_Hossler
03-21-2022, 08:15 AM
@snb --
1. I like your more efficient way, but this below eliminates the .Areas loop
Option Explicit
Sub UnmergeAndFill()
    Dim rData As Range, rBlanks As Range
    Dim ws As Worksheet
    
    For Each ws In ActiveWorkbook.Worksheets
        If ws.Name = "Instructions" Then GoTo NextSheet
        
        ws.Range("A:F").UnMerge
        
        On Error GoTo NextSheet
        Set rData = ws.Cells(1, 1).CurrentRegion
        Set rBlanks = rData.SpecialCells(xlCellTypeBlanks)
        rBlanks.FormulaR1C1 = "=R[-1]C"
        rData.Value = rData.Value
        On Error GoTo 0
            
        If Not ws.AutoFilterMode Then ws.Rows(1).AutoFilter
NextSheet:
    Next
    MsgBox "Done"
End Sub
2. 
NB. You should never use merged cells.
IMVHO, 'Never' is mostly correct, but I'd agree that 97% of the time merged cells only cause trouble
@PH
Yes it does eliminate.
But it takes more calculations.
I am not an adversary to loops, which I am to 'GoTo'-statements.
And almost equally to unnecessary Object variables:
 with ws.Cells(1, 1).CurrentRegion.SpecialCells(4)
   .Value = "=R[-1]C"
   .Value = .Value
 end with
Paul_Hossler
03-22-2022, 03:04 AM
@PH
Yes it does eliminate.
But it takes more calculations.
I am not an adversary to loops, which I am to 'GoTo'-statements.
And almost equally to unnecessary Object variables:
 with ws.Cells(1, 1).CurrentRegion.SpecialCells(4)
   .Value = "=R[-1]C"
   .Value = .Value
 end with
1. Well, the only advantage I see to my 'unnecessary Object variables' is that my way works without generating a lot of #N/A errors
29527
Using the original Test_Unmerge2.xlsx as input, and adding your .Value snippet to the Unmerging and error checking and "Instructions" test
2. I don't see any significant increase in calculations, and I've found that sometimes, and in very specific circumstances, a GoTo can make code more readable without turning it into a plate of spaghetti
Sub UnmergeAndFill_snb()
    Dim ws As Worksheet
    
    For Each ws In ActiveWorkbook.Worksheets
        If ws.Name = "Instructions" Then GoTo NextSheet
        
        ws.Range("A:F").UnMerge
        
        On Error GoTo NextSheet
        
        'snb -----------------------------------------------------------
        With ws.Cells(1, 1).CurrentRegion.SpecialCells(4)
            .Value = "=R[-1]C"
            .Value = .Value
        End With
        'snb -----------------------------------------------------------
        
        On Error GoTo 0
            
        If Not ws.AutoFilterMode Then ws.Rows(1).AutoFilter
NextSheet:
    Next
    
    MsgBox "Done"
End Sub
Here you go:
Sub M_snb()
  For Each it In Sheets
    If it.Name <> "Instructions" Then
      it.Cells.UnMerge
        
      With it.Cells(1).CurrentRegion.Resize(, 4)
        .SpecialCells(4) = "=R[-1]C"
        .Copy
        .PasteSpecial -4163
      End With
    End If
  Next
  Application.CutCopyMode = False
End Sub
No pasta, rigatoni, farfalle, tagliatelle or spaghetti.
Powered by vBulletin® Version 4.2.5 Copyright © 2025 vBulletin Solutions Inc. All rights reserved.