UPDATE:
Situation: I have data that I am trying to consolidate by summing rows based on the first column value (item ID number). If the ID numbers match I want the rows to be added together and the duplicate rows deleted.
I have written the following code and I am experiencing 2 issues:
1. The first time I run the code there is always a few duplicates left that were not consolidated
2. If I run the code again it sums and deletes rows even if they are not duplicates.
Any help would be much appreciated.
Sub ConsolidateRows()
Dim WB As Workbook
Dim WS As Worksheet
Dim iRow As Long
Dim iCol As Long
Dim LastRow As Long
Dim LastCol As Long
Dim duplicate As String
Dim dupRow As Long
Dim cell As Range
Dim i As Integer
'set
Set WB = Workbooks("Book1")
Set WS = WB.Sheets("Data")
LastRow = WS.UsedRange.Rows.Count
LastCol = WS.UsedRange.Columns.Count
'Loop to consolidate, delete the duplicate rows
iRow = 1
While WS.Cells(iRow, 1).Value <> ""
duplicate = Cells(iRow, 1).Value
iRow = iRow + 1
For Each cell In WS.Range("A1:A" & LastRow).Cells
dupRow = cell.Row
If cell.Value = duplicate And iRow <> dupRow Then
For iCol = 3 To LastCol
Cells(iRow, iCol) = Application.WorksheetFunction.Sum(Cells(iRow, iCol), Cells(dupRow, iCol))
Next iCol
WS.Rows(dupRow).Delete
End If
Next cell
Wend
End Sub
You shouldn't delete rows this way, as the range you are looping will start changing and not refer to the desired cells. The quickest fix (without totally restructuring the logic) is to add a variable that holds the rows you want to delete, then delete them all after. My favorite method is using a string with commas as delimiters (easier than using an array)
Dim rowsToDelete As String
For Each cell In WS.Range("A2:A" & LastRow).Cells
jRow = cell.Row
If cell.Value = "Desired Value" Then
'Do Something
rowsToDelete = rowsToDelete & jRow & ","
End If
Next cell
If Len(rowstoDelete) > 1 Then rowstoDelete = Left(rowsToDelete,Len(rowstoDelete) - 1)
If Len(rowstoDelete) > 0 then
For i = UBound(Split(rowstoDelete,",")) to 0 Step -1
WS.Rows(Split(rowstoDelete,",")(i)).Delete
Next i
End If
Related
I have a macro that copies rows from a Sheet named "Template" and pastes them onto the Active Sheet in the next blank row.
However this macro only works when the grouped cells on the Active Sheet are expanded.
If the grouped cells are collapsed, then the macro replaces a previous collapsed group.
I have done some reading and discovered that using a different method to calculate the last row i.e. the MergeArea property would work with collapsed groups but just sure how to apply it.
How could I achieve this with the current code?
This is my code:
Sub Paste_New_Product_from_Template()
Dim copySheet As Worksheet
Dim pasteSheet As Worksheet
Dim LRow As Long, i As Long
Dim StartNumber As Long
Dim varString As String
'~~> This is your input sheet
Set copySheet = ThisWorkbook.Worksheets("Template")
'~~> Variable
varString = copySheet.Cells(2, 2).Value2
'~~> Change this to the relevant sheet
Set pasteSheet = ThisWorkbook.ActiveSheet
'~~> Initialize the start number
StartNumber = 1
With pasteSheet
'~~> Find the last cell to write to
If Application.WorksheetFunction.CountA(.Cells) = 0 Then
LRow = 2
Else
LRow = .Range("A" & .Rows.Count).End(xlUp).Row + 1
'~~> Find the previous number
For i = LRow To 1 Step -1
If .Cells(i, 2).Value2 = varString Then
StartNumber = .Cells(i, 6).Value2 + 1
Exit For
End If
Next i
End If
copySheet.Range("2:" & copySheet.Cells(Rows.Count, 1).End(xlUp).Row).Copy
.Rows(LRow).PasteSpecial Paste:=xlPasteAll
'~~> Set the start number
.Cells(LRow, 6).Value = StartNumber
'~~> Format the number
.Cells(LRow, 6).Value = "'" & Format(StartNumber, "000")
End With
End Sub
Here is the code that uses the MergeArea procedure:
Private Function RowIsEmpty(WSh As Worksheet, Row As Long, StartColumnNumber As Integer, EndColumnNuber As Integer) As Boolean
Dim j As Integer
RowIsEmpty = True
For j = StartColumnNumber To EndColumnNuber
If (WSh.Cells(WSh.Cells(Row, j).MergeArea.Row, WSh.Cells(Row, j).MergeArea.Column) <> "") Then
RowIsEmpty = False
Exit For 'One of columns isn't empty
End If
Next j
End Function
Private Function CalcLastRowNumber(WSh As Worksheet, StartColumnNumber As Integer, EndColumnNuber As Integer) As Long
Dim i As Long
Dim j As Integer
Dim Result As Long
Dim Found As Boolean
Result = 1
For i = 1 To Rows.Count
If RowIsEmpty(WSh, i, StartColumnNumber, EndColumnNuber) And _
RowIsEmpty(WSh, i + 1, StartColumnNumber, EndColumnNuber) And _
RowIsEmpty(WSh, i + 2, StartColumnNumber, EndColumnNuber) Then
'Stop searching
Exit For 'All Columns are empty for current row and for next 2 rows
End If
Result = i
Next i
CalcLastRowNumber = Result
End Function
Sub New_Reviss_Order()
I need some help with comparing values from one column to another and delating it.
so far I have this:
Sub DelateDuplicates()
delArray = Sheets("Save").Range("B1:B") ' saved values
toDelate = Sheets("Validation").Range("B2:B").Value ' values to be checked and delated
lastRow = toDelate.Range("B1000").End(xlUp).Row ' last row
Firstrow = toDelate.Range("B2").End(xlDown).Row ' First row
Dim i As Long
For Lrow = lastRow To Firstrow Step -1
With Worksheets("Validation").Cells(Lrow, "A")
For i = 0 To UBound(delArray) ' arrays are indexed from zero
If Not IsError(.Value) Then
If .Value = delArray(i) Then
.EntireRow.Delete
Exit For
End If
End If
Next
End With
Next Lrow
End Sub
And I do have an error.
"1004 "Application-defined or Object-defined error" "
I have spent 2 days trying to figure it out so far no luck.
Any help will be appreciated.
I modified your code little bit. You can define your first rows and last row the want you want, I have kept it simple for the sake of concept
Option Explicit
Sub DelateDuplicates()
Dim Lrow As Long
Dim delarray()
With Worksheets("Save")
delarray = .Range("B1:B" & .Cells(.Rows.Count, "B").End(xlUp).Row).Value
End With
Dim i As Long
Dim lastrow As Long
Dim firstrow As Long
firstrow = 1
With Worksheets("Validation")
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
For Lrow = lastrow To firstrow Step -1
For i = 1 To UBound(delarray)
If Not IsError(.Cells(Lrow, "A").Value) Then
If .Cells(Lrow, "A").Value = delarray(i, 1) Then
.Cells(Lrow, "A").EntireRow.Delete
Exit For
End If
End If
Next i
Next Lrow
End With
End Sub
You can avoid loops within loops by using a Dictionary Object
Option Explicit
Sub DeleteDuplicates()
Dim wsSave As Worksheet, wsValid As Worksheet
Dim iLastRow As Long, iFirstRow As Long, i As Long, n As Long
Dim dict As Object, key, cell As Range
With ThisWorkbook
Set wsSave = .Sheets("Save")
Set wsValid = Sheets("Validation")
End With
Set dict = CreateObject("Scripting.Dictionary")
' get values to delete from Column B
For Each cell In wsSave.Range("B1", wsSave.Cells(Rows.Count, "B").End(xlUp))
key = Trim(cell)
If Len(key) > 0 Then
dict(key) = cell.Row
End If
Next
' scan Validation sheet and delete matching from Save
With wsValid
iFirstRow = .Cells(2, "B").End(xlDown).Row
iLastRow = .Cells(Rows.Count, "B").End(xlUp).Row
For i = iLastRow To iFirstRow Step -1
key = .Cells(i, "A")
If dict.exists(key) Then
.Rows(i).Delete
n = n + 1
End If
Next
End With
' resutl
MsgBox n & " rows deleted between row " & _
iFirstRow & " and " & iLastRow, vbInformation
End Sub
I'm trying to merge cells in excel using VBA based on the column value. For instance, on row one, wherever the month is the same, merge those cells. I've tried the following code :
Sub Main()
Dim j As Long
For j = 1 To 13
If StrComp(Cells(1, j), Cells(1, j + 1), vbTextCompare) Then
Range(Cells(1, j), Cells(1, j + 1)).Merge
End If
Next j
End Sub
Here, I'm keeping the row fixed as the first row and iterating over the columns and checking if the next cell value is same as the current value. However, in the output it's merging incorrect cells. What am I missing here?
This is easier to understand.
Application.DisplayAlerts = False
With ThisWorkbook.Sheets("Sheet1")
For i = 13 To 2 Step -1 'Loop from the last cell, and stop at the second column
If .Cells(1, i).Value = .Cells(1, i).Offset(, -1).Value Then
.Range(.Cells(1, i), .Cells(1, i).Offset(, -1)).Merge
End If
Next i
End With
Application.DisplayAlerts = True
Should work like this …
Option Explicit
Public Sub MergeSameValuesInRow()
Const iRow As Long = 1 'the row number
Const FirstColumn As Long = 1 'first column with data in iRow
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1") 'define your worksheet
Dim LastColumn As Long 'find last used column in iRow
LastColumn = ws.Cells(iRow, ws.Columns.Count).End(xlToLeft).Column
Dim StartCell As Range 'remember the start cell (first occurence of a new value)
Set StartCell = ws.Cells(iRow, FirstColumn)
Dim iCol As Long
For iCol = FirstColumn + 1 To LastColumn + 1 'loop through columns in iRow
If ws.Cells(iRow, iCol).Value <> StartCell.Value Then 'if value changed …
Application.DisplayAlerts = False 'hide merging messages
ws.Range(StartCell, ws.Cells(iRow, iCol - 1)).Merge 'merge from start cell until one before value change
Application.DisplayAlerts = True
Set StartCell = ws.Cells(iRow, iCol) 'set start cell to the next value
End If
Next iCol
End Sub
It will change this …
into this …
I am trying to format some numbers where some have a leading zero so that I can then search them.
I am needing to format a set of numbers where all are 6 digits and some have a leading zero. I then have a separate code search those numbers for a specific one so the resulting format needs to be searchable. The first code below is the formatting I can't figure out and then the search code. If I simply do an "000000" for formatting I don't believe it works for my search anymore as those now become Special format. Help please?
Sub (First Code)
Dim lngLastRow As Long
lngLastRow = Cells(Rows.Count, "A").End(xlUp).Row
Range("O2:P" & lngLastRow).Select 'specify the range which suits your purpose
With Selection
Selection.NumberFormat = "General"
.Value = .Value
End With
Dim SUPLCD As Range
Set SUPLCD = Range("Q2:Q")
With Selection
Selection.NumberFormat = "#"
Selection.Value = Format(Selection, "000000")
End With
End Sub
Sub Worksheet()
Dim i As Long
Dim j As Long
Dim wsCurrent As Worksheet
Set wsCurrent = ActiveSheet
Dim wsData As Worksheet
Dim rngData As Range
Set wsData = ThisWorkbook.Worksheets("Tempinterior")
Dim wsTempinterior As Worksheet
' Note that .Add will activate the new sheet so we'll
' need to reactivate the worksheet that was previously active
Set wsTempinterior = Worksheets.Add
wsTempinterior.Name = "copy"
' Find the used range in columns A to K and copy over starting
' at cell A1 of wsGalreq
Set rngData = Intersect(wsData.UsedRange, wsData.Range("A:M"))
' Copy over the first row containing column headers
j = 1
rngData.Rows(1).Copy Destination:=wsTempinterior.Cells(j, 1)
For i = 2 To rngData.Rows.Count
' Check cell of column 10 of row i and copy if matched
If rngData.Cells(i, 10).Value = "026572" Or rngData.Cells(i, 10).Value = "435740" Or rngData.Cells(i, 10).Value = "622639" Then
' Copy over to wsDalreq from row j
j = j + 1
rngData.Rows(i).Copy Destination:=wsTempinterior.Cells(j, 1)
End If
Next
End Sub
With above code, the search doesn't pull the entries with those numbers I think because they are formatted as Special.
You don't have to format Col Q to add a 0, you can accomplish your task with out formatting by using Like in your If statement. Because you are not clear about where the values are, you are formatting Col Q but searching Col J, I used Col Q.
Dim wsData As Worksheet
Set wsData = ThisWorkbook.Worksheets("Sheet1") '("Tempinterior")
Dim rngData As Range
Set rngData = Intersect(wsData.UsedRange, wsData.Range("A:M"))
Sheets.Add(After:=Sheets(Sheets.Count)).Name = "copy"
j = 1
rngData.Rows(1).Copy Destination:=Sheets("copy").Cells(j, 1) 'copy headers for rngData
For i = 2 To rngData.Rows.Count
If wsData.Cells(i, 17).Value Like "26572" Or Sheet1.Cells(i, 17).Value = "435740" Or _
Sheet1.Cells(i, 17).Value = "622639" Then
j = j + 1
rngData.Rows(i).Copy Destination:=Sheets("Copy").Cells(j, 1)
End If
Next i
End Sub
First avoid .Select and you will need to loop the change:
Sub first()
Dim lngLastRow As Long
With Worksheets("Sheet1") 'Change to your sheet
lngLastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
With .Range("O2:P" & lngLastRow) 'specify the range which suits your purpose
.NumberFormat = "General"
.Value = .Value
End With
Dim SUPLCD As Range
Set SUPLCD = .Range("Q2:Q" & lngLastRow)
Dim rng As Range
For Each rng In SUPLCD
rng.NumberFormat = "#"
rng.Value = Format(rng.Value, "000000")
Next rng
End With
End Sub
Situation: I have data that I am trying to consolidate by summing rows based on the first column value (item ID number). If the ID numbers match I want the rows to be added together and the duplicate rows deleted.
I have written the following code and I am experiencing 2 issues: 1. The first time I run the code there is always a few duplicates left that were not consolidated 2. If I run the code again it sums and deletes rows even if they are not duplicates.
Any help would be much appreciated.
Sub ConsolidateRows()
Dim WB As Workbook
Dim WS As Worksheet
Dim iRow As Long
Dim iCol As Long
Dim LastRow As Long
Dim LastCol As Long
Dim duplicate As String
Dim dupRow As Long
Dim cell As Range
Dim i As Integer
'set
Set WB = Workbooks("Book1")
Set WS = WB.Sheets("Data")
LastRow = WS.UsedRange.Rows.Count
LastCol = WS.UsedRange.Columns.Count
'Loop to consolidate, delete the duplicate rows
iRow = 1
While WS.Cells(iRow, 1).Value <> ""
duplicate = Cells(iRow, 1).Value
iRow = iRow + 1
For Each cell In WS.Range("A1:A" & LastRow).Cells
dupRow = cell.Row
If cell.Value = duplicate And iRow <> dupRow Then
For iCol = 3 To LastCol
Cells(iRow, iCol) = Application.WorksheetFunction.Sum(Cells(iRow, iCol), Cells(dupRow, iCol))
Next iCol
WS.Rows(dupRow).Delete
End If
Next cell
Wend
End Sub
When deleting rows, always start at the bottom and work your way up.
For example if Column A for rows 1-5 contain:
Alpha
Bravo
Charlie
Delta
Foxtrot
and you delete row 3, you now have
Alpha
Bravo
Delta
Foxtrot
Your loop counter (value 3) was pointing at Charlie before the deletion, but is now pointing at Delta, you then increment your counter to 4, and it's pointing at Foxtrot, therefore you never evaluated whether you needed to delete Delta.
Try this:
'Loop to consolidate, delete the duplicate rows
iRow = LastRow
While WS.Cells(iRow, 1).Value <> ""
duplicate = Cells(iRow, 1).Value
iRow = iRow - 1
For Each cell In WS.Range("A1:A" & LastRow -1).Cells
dupRow = cell.Row
If cell.Value = duplicate And iRow <> dupRow Then
For iCol = 3 To LastCol
Cells(iRow, iCol) = Application.WorksheetFunction.Sum(Cells(iRow, iCol), Cells(dupRow, iCol))
Next iCol
WS.Rows(dupRow).Delete
LastRow = LastRow - 1
End If
Next cell
Wend
*Note: code changes off the top of my head, you may have to make some minor additional tweaks to get it running backwards
Also, please investigate .Find() - it will make your code run significantly faster for finding dups.