Excel Macro Merge Cells Based on Other Merge - vba

I need to do merge and center for over 7,000+ rows. There are 3 columns of the numerous columns that will have data that can be merged. I cannot delete rows. I took a small snippet below to hopefully demonstrate this.
I utilized this macro that I found to merge row A. It works great. The issue is that Column B and C were merging differently. I need the merging to be based on how Column A merged. Column A is unique, it never repeats. Column B and C may repeat so merging must be based on column A.
Code that was used to merge column A:
Sub MergeSameCell()
'Updateby20131127
Dim Rng As Range, xCell As Range
Dim xRows As Integer
xTitleId = "KutoolsforExcel"
Set WorkRng = Application.Selection
Set WorkRng = Application.InputBox("Range", xTitleId, WorkRng.Address, Type:=8)
Application.ScreenUpdating = False
Application.DisplayAlerts = False
xRows = WorkRng.Rows.Count
For Each Rng In WorkRng.Columns
For i = 1 To xRows - 1
For j = i + 1 To xRows
If Rng.Cells(i, 1).Value <> Rng.Cells(j, 1).Value Then
Exit For
End If
Next
WorkRng.Parent.Range(Rng.Cells(i, 1), Rng.Cells(j - 1, 1)).Merge
i = j - 1
Next
Next
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
This allowed me to merge unique codes in column A (over 7000 rows). The next thing I need is to merge the two columns to the right of it based on the merging of the column A.
Example: I need column B & C to be merged based on Column A. I can't do the merge macro listed above that I did for column A because the '50' in column B merges across Column A (01, 02, 03). Instead, I need each to be merged in order regardless of what the next group's value is.
What I have:
What I need:
Any help would be appreciated!

I determined the answer. I will post it here in case someone else faces this issue. I tweaked the original code and saved it in the Macro. Before doing any sorting, Select all rows of column A, then hit F5 to run....
Sub MergeSameCell()
'Updateby20131127
Dim Rng As Range, xCell As Range
Dim xRows As Integer
xTitleId = "Multiple Merge & Center"
Set WorkRng = Application.Selection
Set WorkRng = Application.InputBox("Range", xTitleId, WorkRng.Address,
Type:=8)
Application.ScreenUpdating = False
Application.DisplayAlerts = False
xRows = WorkRng.Rows.Count
For Each Rng In WorkRng.Columns
For i = 1 To xRows - 1
For j = i + 1 To xRows
If Rng.Cells(i, 1).Value <> Rng.Cells(j, 1).Value Then
Exit For
ElseIf Rng.Cells(i, 1).Value = "" Then
Exit For
End If
Next
WorkRng.Parent.Range(Rng.Cells(i, 1), Rng.Cells(j - 1, 1)).Merge
WorkRng.Parent.Range(Rng.Cells(i, 2), Rng.Cells(j - 1, 2)).Merge
WorkRng.Parent.Range(Rng.Cells(i, 3), Rng.Cells(j - 1, 3)).Merge
i = j - 1
Next
Next
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub

Related

Excel VBA - merge cells if values in columns equals

I'm trying to figure out how to implement a macro to get results as follows:
I have no idea how to do it. This is what I've done so far.
I want to have additional column "Action" and if value in column "State" for e.g R1 is empty or "no_fix" then QM (green) else QA (red).
I have data with ~5000 rows
Hi, thanks it works as I expected. However, after testing of my data it turned out that I need to check additional conditions.
1.Additionally for QM and QA:
check in column G if value = "ST"
check in column H if value = 0
2.QA
check in column C if value = "No TC for LM" check in column D if
value = "no state" check in column E if value = "No IPIS" if any of
values = true then QA
Sub MergeSameCell()
'area
Dim Rng As Range, xCell As Range, Test As Range
Dim Rng1 As Range
Dim xRows As Integer
xTitleId = "Merge duplicated cells"
Set WorkRng = Application.Selection
Set WorkRng = Application.InputBox("Range", xTitleId, WorkRng.Address,
Type:=8)
Application.ScreenUpdating = False
Application.DisplayAlerts = False
xRows = WorkRng.Rows.Count
For Each Rng In WorkRng.Columns
For i = 1 To xRows - 1
For j = i + 1 To xRows
'If Rng.Cells(i, 1).Value > 0 And Rng.Cells(j, 1).Value > 0 Then
If Rng.Cells(i, 1).Value <> Rng.Cells(j, 1).Value Then
Exit For
End If
Next
'WorkRng.Parent.Range(Rng.Cells(i, 1), Rng.Cells(j - 1, 1)).Merge
'Text = WorkRng.Parent.Range(Rng.Cells(i, 1), Rng.Cells(j - 1, 1))
i = j - 1
For Each Rng1 In Range(Rng.Cells(i, 1), Rng.Cells(j - 1, 1))
For Z = 1 To 13
'MsgBox i
'MsgBox j
If Rng1.Offset(Z, 1).Value = "no_to_fix" Or Rng1.Offset(Z,
1).Value
= "" Then
'WorkRng.Parent.Range(Rng.Cells(i, 1), Rng.Cells(j - 1,
1)).Merge
Rng1.Cells.Offset(Z, 1).Interior.ColorIndex = 37
'MsgBox "supcio"
End If
Next
Next
Next
Next
WorkRng.VerticalAlignment = xlCenter
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
The following code will do the merging you want and, if I understand what you mean by the second part of the question, will set the first column to be either "QM" (if the fourth column is never anything other than blank or "no_fix") or "QA".
Code assumes you will use the InputBox to select a range containing four columns, the first being the column that will contain "QM" or "QA", the second being the column that is your "Req" column, and the fourth being your "State" column. (The code never looks at what is in the third column.)
Sub MergeSameCell()
Dim WorkRng As Range
xTitleId = "Merge duplicated cells"
Set WorkRng = Selection
Set WorkRng = Application.InputBox("Range", xTitleId, WorkRng.Address, Type:=8)
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Dim startRow As Long
Dim endRow As Long
Dim r As Long
Dim isQM As Boolean
'Use "startRow" to keep track of the start of each block
startRow = 1
With WorkRng
'Loop through each row in the selected range
For endRow = 1 To .Rows.Count
If .Cells(endRow + 1, 2).Value <> .Cells(startRow, 2).Value Then
'Only do something if the next row has a different value in the second column
'merge rows in the first and second columns
.Worksheet.Range(.Cells(startRow, 1), .Cells(endRow, 1)).MergeCells = True
.Worksheet.Range(.Cells(startRow, 2), .Cells(endRow, 2)).MergeCells = True
'Check for "no_fix" or blank
isQM = True ' Assume it is a "QM" until we determine it isn't
For r = startRow To endRow
If .Cells(r, 4).Value <> "" And .Cells(r, 4).Value <> "no_fix" Then
'If the 4th column is not blank and is not "no_fix", it isn't a "QM"
isQM = False
Exit For
End If
Next
'Update column 1 to show QM or QA
With .Cells(startRow, 1)
If isQM Then
.Value = "QM"
.Interior.Color = vbGreen
Else
.Value = "QA"
.Interior.Color = vbRed
End If
End With
'Point to start of next block
startRow = endRow + 1
End If
Next
End With
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub

Deleting "empty" rows when they just "appear empty"

I can not manage to cleanse my data of the "empty" rows. There is no problem in deleting the "0" but those cells which are empty are not empty but have something like "null strings" in it.
Sub Reinigung()
Application.ScreenUpdating = False 
Application.EnableEvents = False 
ListeEnde3 = ThisWorkbook.Sheets("input").Cells(Rows.Count, 1).End(xlUp).Row
For Zeile1 = 2 To ListeEnde3
If ThisWorkbook.Sheets("input").Cells(Zeile1, 14) = "0" Or ThisWorkbook.Sheets("2018").Cells(Zeile1, 14) = "" Then
ThisWorkbook.Sheets("input").Rows(Zeile1).Delete
Zeile1 = Zeile1 - 1
Else
End If
Next
' ThisWorkbook.Sheets("import").Columns(14).SpecialCells(xlCellTypeBlanks).EntireRow.Delete
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
That code just freezes my excel, if i leave out the
thisWorkbook.Sheets("2018").Cells(Zeile1, 14) = ""
part, it works and deletes all rows, where colum 14 contains a "0".
If I check the cells which appear blank with =isblank it returns "false". There is no "space" in the cell and no " ' ".
What to do?
edit
After the first tips my code looks like this now:
Sub Reinigung()
Dim ListeEnde3 As Long
Dim Zeile1 As Long
Application.ScreenUpdating = False 
Application.EnableEvents = False 
ListeEnde3 = ThisWorkbook.Sheets("import").Cells(Rows.Count, 1).End(xlUp).Row
For Zeile1 = ListeEnde3 To 2 Step -1
Set rngX = ThisWorkbook.Sheets("import").Cells(Zeile1, 14)
If (rngX = "0" Or rngX = "") Then 'or rngY = vbNullString
ThisWorkbook.Sheets("import").Rows(Zeile1).Delete
End If
Next Zeile1
' ThisWorkbook.Sheets("import").Columns(14).SpecialCells(xlCellTypeBlanks).EntireRow.Delete
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
Excel still crashes / freezes (I waited for 5 minutes) but since the code runs "smoothly" with F8 I wanted to give it a shot with less data: It works!
If I am not reducing the data there are ~ 70000 rows to check. I let it run on 720 rows and it worked.
Any way to tweak the code in a way that it can handle the 70000+ rows? I didn't think that it would be too much.
Thanks!
You can use AutoFilter and delete the visible rows (not tested) :
Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets("import")
ws.UsedRange.AutoFilter 14, Array("=0", "="), xlFilterValues
ws.UsedRange.Offset(1).EntireRow.Delete
ws.AutoFilterMode = False
Another way is to simply use internal arrays and write out the new data set which has valid rows.
It is very fast.
If your dataset has formulas then you'll have to use extra code, but if it's constants only, then the below should do:
Sub Reinigung()
'Here I test with column E to Z, set Ranges appropriately
Application.ScreenUpdating = False
Application.EnableEvents = False
Dim ListeEnde3 As Long, x As Long, y As Long
'last row of data - set to column of non-blank data
ListeEnde3 = ThisWorkbook.Sheets("import").Cells(Rows.Count, 5).End(xlUp).Row
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("import")
Dim startCell As Range
'set to whatever cell is the upper left corner of data
Set startCell = ThisWorkbook.Sheets("import").Range("E1")
Dim arr As Variant, arrToPrint() As Variant
'Get rightmost column of data instead of hardcoding to "Z"
'write dataset into an array
arr = ws.Range(startCell, ws.Range("Z" & ListeEnde3)).Value
x = UBound(arr) - LBound(arr) + 1 'num of rows of data
y = UBound(arr, 2) - LBound(arr, 2) + 1 'num of columns of data
ReDim arrToPrint(1 To x, 1 To y) 'array to hold valid/undeleted data
Dim i As Long, j As Long, printCounter As Long, arrayColumnToCheck as Long
arrayColumnToCheck = 14 - startCell.Column + 1 '14 is column N
For i = 1 To x
If arr(i, arrayColumnToCheck ) <> 0 And arr(i, arrayColumnToCheck ) <> vbNullString Then
printCounter = printCounter + 1
For j = 1 To y
'put rows to keep in arrToPrint
arrToPrint(printCounter, j) = arr(i, j)
Next j
End If
Next i
'Print valid rows to keep - only values will print - no formulas
startCell.Resize(printCounter, y).Value = arrToPrint
'Delete the rows with zero & empty cells off the sheet
startCell.Offset(printCounter).Resize(ListeEnde3 - printCounter, y).Delete xlShiftUp
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
You can add IsEmpty to your code to check the cells filling
Sub Reinigung()
Application.ScreenUpdating = False
Application.EnableEvents = False
ListeEnde3 = ThisWorkbook.Sheets("input").Cells(Rows.Count, 1).End(xlUp).Row
For Zeile1 = 2 To ListeEnde3
Set rngX = ThisWorkbook.Sheets("input").Cells(Zeile1, 14)
Set rngY = ThisWorkbook.Sheets("2018").Cells(Zeile1, 14)
If (rngX = "0" And (Not IsEmpty(rngX))) Or (rngY = "") Then
ThisWorkbook.Sheets("input").Rows(Zeile1).Delete
Zeile1 = Zeile1 - 1
End If
Next
' ThisWorkbook.Sheets("import").Columns(14).SpecialCells(xlCellTypeBlanks).EntireRow.Delete
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
NEVER a good idea to alter a loop counter: Zeile1 = Zeile1 - 1
Instead start at the end and use Step -1 in your loop to work backward.
You are in a infinite loop because the loop doesnt move forward. If Zeile=3 and there is a "" in row3 in the '2018' sheet, then it will always be stuck on the Zeile1 = 3 line. You will always be coming back to that "" on row 3 in '2018'sheet.
For Zeile1 = ListeEnde3 To 2 Step -1
Set rngX = ThisWorkbook.Sheets("input").Cells(Zeile1, 14)
Set rngY = ThisWorkbook.Sheets("2018").Cells(Zeile1, 14)
If (rngX = "0" Or rngY = "") Then 'or rngY = vbNullString
ThisWorkbook.Sheets("input").Rows(Zeile1).Delete
End If
Next Zeile1

Consolidate several rows into a single row vba

I need to create a sub to consolidate some data. I have several rows (anywhere from 4k to 20k) that I need to consolidate and sum the values from each column (from C to N).
The input data looks like this:
input
For the output, it should sum the columns for each SKU (Column A) and delete the rest.
Like this:
output
It should be simple enough, but I can’t seem to come up with an appropriate solution. I tried using an array with a scripting dictionary but I can’t figure out how to store more than a single value for each key. Sample (unfinished) code:
Dim sArray As Variant
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
sArray = Range(currentRange).Value
For i = 1 To UBound(sArray, 1)
For j = 3 To UBound(sArray, 2)
If dict.exists(sArray(i, 1)) = False Then
dict.Add sArray(i, 1), sArray(i, j)
Else
'this part is very wrong:
dict(sArray(i, 1)) = dict(sArray(i, j)) + sArray(i, j)
End If
Next
Next
Thank you very much in advance!
Try this, It sums the values in Column Q:AB then paste them back and removes the duplicates.
Sub dupremove()
Dim ws As Worksheet
Dim lastrow As Long
Set ws = Sheets("Sheet12") ' Change to your sheet
With ws
lastrow = .Range("A" & .Rows.Count).End(xlUp).Row
With .Range("C2:N" & lastrow)
.Offset(, 14).FormulaR1C1 = "=SUMIF(C1,RC1,C[-14])"
.Value = .Offset(, 14).Value
.Offset(, 14).ClearContents
End With
With .Range("A1:N" & lastrow)
.Value = .Value
.RemoveDuplicates 1, xlYes
End With
End With
Before:
After:
I came up with the following solution instead and it took 30 seconds to run it (not entirely my own idea, borrowed some code from someplace else):
Sub dupes()
Dim MyRange As Range
Dim RowNum As Long
RowNum = 1
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
Set MyRange = Range("A2:N14200") 'for test only, on the real sub it's dynamic
MyRange.Sort key1:=Range("A2"), order1:=xlAscending
For Each Row In MyRange
With Cells
While Cells(RowNum, 1) = Cells(RowNum + 1, 1) And Cells(RowNum + 1, 1) <> "" 'very important the second condition or it will continue to loop forever
For i = 3 To 14
Cells(RowNum, i) = Cells(RowNum, i) + Cells(RowNum + 1, i)
Next
Rows(RowNum + 1).EntireRow.Delete
Wend
End With
RowNum = RowNum + 1
Next
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
It's kinda messy but it does the trick. Thanks to everyone!

Can someone help me optimize the VBA loop in excel

My worksheet have 6000 rows. This loop takes me more than 20minutes to finish. It is too long for me because I have many columns to run this loop. Can someone help me?
Dim i As Integer
For i = ActiveCell.Row To 5771
If Cells(i, ActiveCell.Column - 1).Value = 0 And Cells(i, ActiveCell.Column).Value = "" Then
Cells(i, ActiveCell.Column).Value = 0
ElseIf Cells(i, ActiveCell.Column - 1).Value = 1 Then
Range(Cells(i, ActiveCell.Column), Cells(i + 9, ActiveCell.Column)).Value = 1
ElseIf Cells(i, ActiveCell.Column - 1).Value = -1 Then
Range(Cells(i, ActiveCell.Column), Cells(i + 9, ActiveCell.Column)).Value = -1
End If
Next i
It is hard to tell exactly what you're trying to do. The loop structure you're using appears to be very inefficient: you're looping over rows in a range, and performing some evaluation/logic test on each cell.
If the adjacent (to the left) cell's value is 1 or -1, then you're filling the cell and the next 9 cells with that value. But then when you hit the Next in your loop, you will perform your test on those cells. So, either you should not be filling a value down 10 rows, or you should avoid testing those rows since presumably nothing needs to be done with them (otherwise you should not have filled them in in the first place!) So you can see why I am a little confused.
In any case, I assume that you do not need to test the 9 rows beneath when the Cells(i, ActiveCell.Column - 1).Value = 1 or Cells(i, ActiveCell.Column - 1).Value = -1.
I have not tested either of these so they may have some typos/etc.
The fastest method is to perform manipulations on yoru data in memory only. You can store the range's values in an array, and perform the operations on the array, and then "write" the values back to the worksheet in a single statement. Looping in memory is much faster than looping and writing on the worksheet.
Dim rng as Range
Dim arr as Variant
Dim val as Variant
Dim r as Long, i As Integer
Set rng = Range(Cells(ActiveCell.Row, ActiveCell.Column -1).Address, Cells(5771, ActiveCell.Column).Address)
'store the range values in a variant array:
' this will be of the structure arr(_row#_, _column#_)
arr = rng.Value
For r = 1 to UBound(arr, 1) 'Loop until last row in range/array
'arr(r,1) represents the first column of the range -- i.e., the column to left of ActiveCell
' so we can use a Case statement to check this value of either 0, 1, or -1.
Select Case arr(r, 1)
Case 0
'if the adjacent left cell = 0 AND this cell's value = ""
' then make this cell's value = 0.
If arr(r, 2) = "" Then arr(r, 2) = 0
Case 1, -1
For i = 0 to 10
'if the value is 1 or -1, puts the in this cell AND the next 9 cells
arr(r + i, 2) = arr(r, 1)
Next
'increment our iterator variable
r = r + 9
Case Else
'Do nothing...
End Select
Next
'put the transformed values in to the worksheet
rng.Value = arr
That is basically the same as this, which uses the worksheet object/cells in the loop. It more closely resembles your loop, but it will also be less efficient than the above.
'Alternatively, but this will be slower:
Dim rng as Range
Dim cl as Range
Dim i as Integer
Set rng = Range(Cells(ActiveCell.Row, ActiveCell.Column -1).Address, Cells(5771, ActiveCell.Column).Address)
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
For each cl in rng.Cells
With cl
Select Case .Offset(0, -1).Value
Case 0
If .Value = "" Then .Value = 0
Case 1, -1
.Resize(10,1).Value = .Offset(0, -1).Value
Case Else
'Do nothing
End Select
End With
Next
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic

Concatenate Column Values Row by Row Dynamically

My code concatenates the values of the columns of the first row of table "Data" and writes the result in table "Insert" cell A1.
The error is that the concatenation result of next row is added at the end of the first result in table "Insert" A1 and so on. The results should be written in table "Insert" in column A row by row too.
What is wrong with my code?
Sub InsertStatementRow()
Dim x As String
Dim rng As Range
Dim cel As Range
Dim ColMax As Integer
Dim i As Long
Sheets("Data").Select
ColMax = Cells(1, Columns.Count).End(xlToLeft).Column
row = 1
Do While Cells(row, "A").Value <> ""
With Worksheets("Data")
i = 1
Set rng = Range(.Cells(i, 1), .Cells(i, ColMax))
End With
For Each cel In rng
x = x & cel.Value
Next
Sheets("Insert").Cells(i, 1).Value = x
row = row + 1
Loop
End Sub
Thanks for your help!
i is being set as "1" each time the Do While loop begins, so the rng variable will always refer to row 1 of the Data worksheet. It's also copying to row 1 on the Insert worksheet. Change the i to row and it should work:
Do While Cells(row, "A").Value <> ""
With Worksheets("Data")
Set rng = Range(.Cells(row, 1), .Cells(row, ColMax))
End With
x = ""
For Each cel In rng
x = x & cel.Value
Next
Sheets("Insert").Cells(row, 1).Value = x
row = row + 1
Loop
you set
i=1
before
Set rng = Range(.Cells(i, 1), .Cells(i, ColMax))
but you seem to bee working with variable row. So delete/comment the line i=1 and change Set rng
Set rng = Range(.Cells(row, 1), .Cells(row, ColMax))
hope it solves your problem.