PDA

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!

snb
03-21-2022, 03:23 AM
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

snb
03-22-2022, 01:23 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

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

snb
03-22-2022, 07:28 AM
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.