I am trying to eliminate line items that cancel each other out.
For example, below the two rows that add to zero would be deleted (i.e., 87.1 and -87.1).
-87.1
890
87.1
898989
The code that I am using mostly works but in cases where there are numerous lines with the same values it is deleting all of them instead of just one matching value per observation. For example, below, I would want it to cancel out two of the -87.1s and two of the 87.1s but one would be leftover because there is no number directly offsetting it.
-87.1
890
87.1
898989
87.1
-87.1
-87.1
Sub x()
Dim n As Long, rData As Range
Application.ScreenUpdating = False
n = Range("C" & Rows.Count).End(xlUp).Row
Range("AV2:AV" & n).Formula = "=IF(I2=0,0,COUNTIFS($C$2:$C$" & n & ",C2,$I$2:$I$" & n & ",-I2))"
With ActiveSheet
.AutoFilterMode = False
.Rows(1).AutoFilter field:=48, Criteria1:=">0"
With .AutoFilter.Range
On Error Resume Next
Set rData = .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If Not rData Is Nothing Then
rData.EntireRow.Delete shift:=xlUp
End If
End With
.AutoFilterMode = False
End With
Application.ScreenUpdating = True
End Sub
I think you need something like this:
Sub DeleteOppositeNumbers()
Dim Fnd As Range, r As Long
'By: Abdallah Ali El-Yaddak
Application.ScreenUpdating = False
'Loop through the column bottom to top.
For r = Range("C" & Rows.Count).End(xlUp).Row To 2 Step -1
If Cells(r, 3).Value > 0 Then 'If the value is positive
'Sreach for it's opposite
Set Fnd = Columns(3).Find(-Cells(r, 3).Value, LookAt:=xlWhole)
'If found, delete both.
If Not Fnd Is Nothing Then Rows(r).Delete: Fnd.EntireRow.Delete
End If
Next
'Just to restore normal behaviour of sreach
Set Fnd = Columns(3).Find(Cells(3, 2).Value, LookAt:=xlPart)
Application.ScreenUpdating = True
End Sub
Perhaps Something Simpler:
Sub x()
Dim ar() As Variant
Dim i As Integer
Dim j As Integer
Dim n As Integer
n = Range("C" & Rows.Count).End(xlUp).Row
Range("AV2:AV" & n).Formula = "=IF(I2=0,0,COUNTIFS($C$2:$C$" & n & ",C2,$I$2:$I$" & n & ",-I2))"
ar = ActiveSheet.Range("AV2:AV" & last).Value
For i = LBound(ar) To UBound(ar)
For j = LBound(ar) To UBound(ar)
If i <> j Then
If ar(i, 1) = ar(j, 1) Then
ar(i, 1) = ""
ar(j, 1) = ""
End If
End If
Next
Next
For i = LBound(ar) To UBound(ar)
ActiveSheet.Range("AV" & i + 1).Value = ar(i, 1)
Next
ActiveSheet.Range("AV2:AV" & last).SpecialCells(xlCellTypeBlanks).Delete xlUp
End Sub
I have tried and tested this one.
You could try:
Option Explicit
Sub test()
Dim arr As Variant
Dim LastRow As Long, i As Long, j As Long
With ThisWorkbook.Worksheets("Sheet1")
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
arr = Range("A1:A" & LastRow)
For i = UBound(arr) To LBound(arr) Step -1
For j = UBound(arr) - 1 To LBound(arr) Step -1
If arr(i, 1) + arr(j, 1) = 0 Then
.Rows(i).EntireRow.Delete
.Rows(j).EntireRow.Delete
Exit For
End If
Next j
Next i
End With
End Sub
Related
I'm trying to run a compare though multiple sheets.
I get
Runtime error 9 subscript out of range
Sub Comp_TEST()
Dim ar As Variant
Dim var()
Dim i As Long
Dim n As Long
Dim Last_Row As Long
Dim WS As Worksheet
For Each WS In ActiveWorkbook.Worksheets
If WS.Name <> "GALVANISED" And WS.Name <> "ALUMINUM" And WS.Name <> "LOTUS" And WS.Name <> "TEMPLATE" And WS.Name <> "SCHEDULE CALCULATIONS" And WS.Name <> "TRUSS" And WS.Name <> "DASHBOARD CALCULATIONS" And WS.Name <> "GALVANISING CALCULATIONS" Then
WS.Range("D3:D1000").Copy
WS.Range("O3").PasteSpecial xlPasteValues
WS.Range("K3:K1000").Copy
WS.Range("N3").PasteSpecial xlPasteValues
Application.CutCopyMode = False
ar = WS.Range("N3").CurrentRegion
ReDim var(1 To UBound(ar, 1), 1 To 1)
With CreateObject("scripting.dictionary")
.comparemode = 1
For i = 1 To UBound(ar, 1)
.Item(ar(i, 2)) = Empty
Next
For i = 1 To UBound(ar, 1)
If Not .exists(ar(i, 1)) Then
n = n + 1
var(n, 1) = ar(i, 1) 'error happens here
End If
Next
End With
WS.[P3].Resize(n).Value = var
Erase var
ReDim var(1 To UBound(ar, 1), 1 To 1)
Last_Row = WS.Range("D2").End(xlDown).Offset(1).Row
WS.Range("P3:P1000").Copy
WS.Range("D" & Last_Row).PasteSpecial xlPasteValues
WS.Range("N3:P1000").ClearContents
End If
Next WS
End Sub
The following works but then I need to make a Sub for at the moment 26 sheets which could be more later. I don't want to make another Sub each time that happens.
Or I may also need to delete a sheet then I would have delete that Sub.
Sub Comp_ALL_VANS()
Dim ar As Variant
Dim var()
Dim i As Long
Dim n As Long
Dim Last_Row As Long
Worksheets("ALL VANS").Range("D3:D1000").Copy
Worksheets("ALL VANS").Range("O3").PasteSpecial xlPasteValues
Worksheets("ALL VANS").Range("K3:K1000").Copy
Worksheets("ALL VANS").Range("N3").PasteSpecial xlPasteValues
Application.CutCopyMode = False
ar = Worksheets("ALL VANS").Range("N3").CurrentRegion
ReDim var(1 To UBound(ar, 1), 1 To 1)
With CreateObject("scripting.dictionary")
.comparemode = 1
For i = 1 To UBound(ar, 1)
.Item(ar(i, 2)) = Empty
Next
For i = 1 To UBound(ar, 1)
If Not .exists(ar(i, 1)) Then
n = n + 1
var(n, 1) = ar(i, 1)
End If
Next
End With
Worksheets("ALL VANS").[P3].Resize(n).Value = var
Last_Row = Worksheets("ALL VANS").Range("D2").End(xlDown).Offset(1).Row
Worksheets("ALL VANS").Range("P3:P1000").Copy
Worksheets("ALL VANS").Range("D" & Last_Row).PasteSpecial xlPasteValues
Worksheets("ALL VANS").Range("N3:P1000").ClearContents
End Sub
Option Explicit
Sub Comp_TEST()
Dim ws As Worksheet, n As Long
Dim arSkip
arSkip = Array("GALVANISED", "ALUMINUM", "LOTUS", "TEMPLATE", "SCHEDULE CALCULATIONS", _
"TRUSS", "DASHBOARD CALCULATIONS", "GALVANISING CALCULATIONS")
For Each ws In ActiveWorkbook.Worksheets
If IsError(Application.Match(ws.Name, arSkip, 0)) Then
Call process(ws)
n = n + 1
Else
Debug.Print "Skipped " & ws.Name
End If
Next
MsgBox n & " sheets processed", vbInformation
End Sub
Sub process(ws As Worksheet)
Dim dict As Object, k As String, arK, arD, arNew
Dim n As Long, i As Long, LastRowD As Long, LastRowK as Long
Set dict = CreateObject("scripting.dictionary")
dict.comparemode = 1
With ws
LastRowK = .Cells(.Rows.Count, "K").End(xlUp).Row
If LastRowK < 4 Then LastRowK = 4 ' ensure 2 cells for array
arK = .Range("K3:K" & LastRowK)
LastRowD = .Cells(.Rows.Count, "D").End(xlUp).Row
If LastRowD <= 3 Then
arD = .Range("D3:D4") ' ensure 2 cells for array
If LastRowD < 2 Then LastRowD = 2
Else
arD = .Range("D3:D" & LastRowD)
End If
End With
' array for new
ReDim arNew(1 To UBound(arK), 1 To 1)
' fill dictionary from col D
For i = 1 To UBound(arD)
k = arD(i, 1)
If dict.exists(k) Then
MsgBox "Duplicate key '" & k & "' at D" & i + 2, vbCritical, "Error " & ws.Name
Exit Sub
ElseIf Len(k) > 0 Then
dict.Add k, i
End If
Next
' compare col K with col D
n = 0
For i = 1 To UBound(arK)
k = arK(i, 1)
If Not dict.exists(k) Then
n = n + 1
arNew(n, 1) = k
End If
Next
' result
If n > 0 Then
ws.Range("D" & LastRowD + 1).Resize(n) = arNew
End If
End Sub
Assume I have data in column (A) like the following:
Names
Yasser
Hany
Ahmed
Reda
Ahmed
Yasser
Reda
Yasser
Duplicates can be detected using such a code
Sub Find_Duplicates()
Dim e, x(), dic As Object, cel As Range, lr As Long, i As Long
Application.ScreenUpdating = False
lr = Cells(Rows.Count, 1).End(xlUp).Row
With CreateObject("Scripting.Dictionary")
.CompareMode = vbTextCompare
For Each cel In Range("A1:A" & lr)
If Not .Exists(cel.Value) Then
.Item(cel.Value) = cel.Value & "^" & cel.Address(0, 0)
Else
.Item(cel.Value) = Split(.Item(cel.Value), "^")(0) & " | " & cel.Value & "^" & Split(.Item(cel.Value), "^")(1) & " | " & cel.Address(0, 0)
End If
Next cel
If .Count Then
ReDim x(1 To .Count, 1 To 2)
For Each e In .Keys
If InStr(.Item(e), "|") > 0 Then
i = i + 1
x(i, 1) = Split(.Item(e), "^")(0)
x(i, 2) = Split(.Item(e), "^")(1)
End If
Next e
End If
Columns("F:G").ClearContents
Range("F1:G1").Value = Array("Duplicate Entries", "Address")
If i > 0 Then Range("F2").Resize(i, 2).Value = x
End With
Application.ScreenUpdating = True
End Sub
The output would be in columns F & G like that
What I am trying to get is like that (in Column B)
If you decide on formulas instead, then you could use:
Formula in B2:
=IF(COUNTIF(A$2:A$9,A2)>1,"Duplicate"&MATCH(A2,UNIQUE(FILTER(A$2:A$9,COUNTIF(A$2:A$9,A$2:A$9)>1)),0),"")
Non-ExcelO365 users could use:
=IF(COUNTIF(A$2:A$9,A2)>1,IF(MATCH(A2,A$1:A$9,0)=ROW(),"Duplicate"&MAX(IFERROR(--MID(B$1:B1,10,99),0))+1,INDEX(B$1:B1,MATCH(A2,A$1:A$9,0))),"")
Be sure to accept the formula through CtrlShiftEnter
You could modify your subroutine like this:
Sub Find_Duplicates()
Dim e, x(), dic As Object, cel As Range, lr As Long, i As Long, j As Long, arr() As String
Application.ScreenUpdating = False
lr = Cells(Rows.Count, 1).End(xlUp).Row
With CreateObject("Scripting.Dictionary")
.CompareMode = vbTextCompare
For Each cel In Range("A1:A" & lr)
If Not .Exists(cel.Value) Then
.Item(cel.Value) = cel.Value & "^" & cel.Address(0, 0)
Else
.Item(cel.Value) = Split(.Item(cel.Value), "^")(0) & " | " & cel.Value & "^" & Split(.Item(cel.Value), "^")(1) & " | " & cel.Address(0, 0)
End If
Next cel
If .Count Then
ReDim x(1 To .Count, 1 To 2)
For Each e In .Keys
If InStr(.Item(e), "|") > 0 Then
i = i + 1
arr = Split(Split(.Item(e), "^")(1), "|")
For j = LBound(arr) To UBound(arr)
Set cel = Range(Trim(arr(j)))
Cells(cel.Row, cel.Column + 1).Value = "Duplicate" & CStr(i)
Next j
End If
Next e
End If
End With
Application.ScreenUpdating = True
End Sub
Here, the cell addresses are split from each item and into an array of strings. Each cell address is used to move one cell to the right and then write the duplicate number there.
Im trying to detect duplicates on column("G") of my input workbook and by using lastrow of its data at column("E") to merge upwards by using & "" & after which it will delete the entireRow and this process continue until there are no more duplicates.
I tried and also look up for many codes including delete and duplicates but I am still having trouble.
Dim myCell As Range, myRow As Integer, myRange As Range, myCol As Integer, X As Integer
'Count number column
Set wsInput = Workbooks("InputB.xls").Worksheets("HC_MODULAR_BOARD_20180112")
myCol = Range(Cells(3, 7), Cells(3, 7).End(xlDown)).Count
'Loop each column to check duplicate values & highlight them.
For X = 3 To myRow
Set myRange = Range(Cells(2, X), Cells(myRow, X))
For Each myCell In myRange
If Workbooks("InputB.xls").Worksheets("HC_MODULAR_BOARD_20180112").CountIf(myRange, myCell.Value) > 1 Then
myCell.Interior.ColorIndex = 3
End If
Next
Next
' allow values at Column"E" to merge upwards and delete all duplicate and its row (missing)
I have no clue how to delete after copying data on top of the column. Someone please help.
Many Thanks,
Adrian
You could try:
Option Explicit
Sub test()
Dim LastRow As Long, i As Long, y As Long, Counter As Long
Dim SearchValue As String, AddValue As String
With ThisWorkbook.Worksheets("Sheet1") ' Always select your worksheet name
LastRow = .Cells(.Rows.Count, "C").End(xlUp).Row
Counter = 0
AddValue = ""
SearchValue = ""
For i = LastRow To 3 Step -1
SearchValue = .Range("C" & i).Value
If SearchValue <> "" Then
If Application.WorksheetFunction.CountIf(.Range("C3:C" & LastRow), SearchValue) > 1 Then
For y = i To 3 Step -1
If .Range("C" & y).Value = SearchValue Then
If AddValue = "" Then
AddValue = .Range("E" & y).Value
Else
AddValue = AddValue & ", " & .Range("E" & y).Value
.Rows(y).EntireRow.Delete
Counter = Counter + 1
End If
End If
Next y
.Range("E" & i - Counter).Value = AddValue
AddValue = ""
SearchValue = ""
Counter = 0
End If
End If
Next i
End With
End Sub
I have a table which contains merged cells both column and rows as shown in attached picture. I want to unmerge "Only" rows while leaving columns merged. Consider the following snippet of table. In the image attached "Contract
For y = 1 To lRow
p = 1
c = y
d = 1
z = lRow + y
t = Cells(y, 1).Value
For x = 1 To t
Cells(z, p).Value = Cells(c, d).Value
Cells(c, d).Select
' Debug.Print
Selection.End(xlToRight).Select
c = ActiveCell.Row
d = ActiveCell.Column
p = p + 1
Next
Next
Sub ColorMergedCells()
Dim c As Range
Dim startcolumn, endcolumn, startrow, endrow As Long
For Each c In ActiveSheet.UsedRange
If c.MergeCells And c.MergeArea.Rows.Count >= 2 Then
c.Interior.ColorIndex = 28
With c.MergeArea.Rows
.UnMerge
' .Formula = c.Formula
End With
'
'startcolumn = ActiveCell.Column
'endcolumn = Selection.Columns.Count + startcolumn - 1
'startrow = ActiveCell.Row
'endrow = Selection.Rows.Count + startrow - 1
End If
Next
End Sub
Based on your snapshot of requirements , I have wrote a very simple code which shall appear to be crude but I have kept it this way so that you can adjust its various elements as per your actual data. Sample data taken by me and results obtained are shown in the snapshot pasted below, which is followed by code.
Sub Merge_unmerge()
Dim wb As Workbook
Dim ws As Worksheet
Dim rng As Range
Dim cell As Range
Dim LastRow As Long
Dim LastCol As Long
Set wb = ThisWorkbook
Set ws = wb.Sheets(1)
With ws
LastRow = .Cells(.Rows.Count, "B").End(xlUp).Row
LastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
End With
Set rng = ws.Range("A1:D" & LastRow)
For Each cell In rng
cell.UnMerge
Next cell
For i = 2 To LastRow
If Range("A" & i) = "" Then
Range("A" & i).Value = Range("A" & i - 1).Value
End If
Next i
For i = 2 To LastRow
If Range("D" & i) = "" Then
Range("D" & i).Value = Range("D" & i - 1).Value
End If
Next i
For i = 1 To LastRow Step 2
Range("B" & i & ":C" & i).Merge
Range("B" & i & ":C" & i).HorizontalAlignment = xlCenter
Next i
End Sub
Never mind. I solved for the issue at hand. Posting if it helps others.
Sub ColorMergedCells()
Dim c As Range
Dim startcolumn, endcolumn, startrow, endrow As Long
For Each c In ActiveSheet.UsedRange
If c.MergeCells And c.MergeArea.Rows.Count >= 2 Then
c.Interior.ColorIndex = 28
startcolumn = c.Column
endcolumn = c.MergeArea.Columns.Count + startcolumn - 1
startrow = c.Row
endrow = c.MergeArea.Rows.Count + startrow - 1
With c.MergeArea.Rows
.UnMerge
.Formula = c.Formula
End With
For J = startrow To endrow
Application.DisplayAlerts = False
Range(Cells(J, startcolumn), Cells(J, endcolumn)).Merge
Application.DisplayAlerts = True
Next
End If
Next
End Sub
Example of my dataset:
blank
1
2
blank
3
4
5
blank
6
I want to merge all cells below a blank cell into the blank cell, but stop counting when it reaches the next blank cell.
End result should look like this, with the strings concatenated
12
345
6
I'm currently trying to create an array with 1s and 2s with 2 meaning its a blank cell, then counting the 1s and merging them. I don't know if this will work or if there is an easier way to do this.
This requires you to select the area you want to merge, starting with the first blank cell and ending with the last cell with a value. It will delete entire rows; not sure if that's what you wanted:
Sub MergeConstantsIntoEmpties()
Dim BlankCells As Excel.Range
Dim ConstantCells As Excel.Range
Dim i As Long
Dim MungedContents As String
With Selection
Set BlankCells = .SpecialCells(xlCellTypeBlanks)
Set ConstantCells = .SpecialCells(xlCellTypeConstants)
End With
For i = 1 To BlankCells.Areas.Count
If ConstantCells.Areas(i).Count = 1 Then
MungedContents = ConstantCells.Areas(i).Value
Else
MungedContents = Join(Application.WorksheetFunction.Transpose(ConstantCells.Areas(i).Value))
End If
BlankCells.Areas(i).Value = MungedContents
Next i
ConstantCells.EntireRow.Delete
End Sub
If we start with:
and run this macro:
Sub PileOn()
Dim N As Long, st As String
Dim i As Long, v As Variant
N = Cells(Rows.Count, "A").End(xlUp).Row
For i = N To 1 Step -1
v = Cells(i, 1).Value
If v <> "" Then
st = st & v
Cells(i, 1).Delete shift:=xlUp
Else
Cells(i, 1).Value = st
st = ""
End If
Next i
End Sub
We end up with:
EDIT#1:
To fix the order of the concatenated cells use this instead:
Sub PileOn()
Dim N As Long, st As String
Dim i As Long, v As Variant
N = Cells(Rows.Count, "A").End(xlUp).Row
For i = N To 1 Step -1
v = Cells(i, 1).Value
If v <> "" Then
st = v & st
Cells(i, 1).Delete shift:=xlUp
Else
Cells(i, 1).Value = st
st = ""
End If
Next i
End Sub
Here is my take on it.
Sub JoinBetweenTheLines()
Dim X As Long
X = 1
Do Until X >= Range("A" & Rows.Count).End(xlUp).Row
If Range("A" & X).text = "" Then
Range("A" & X).Delete xlUp
ElseIf Range("A" & X).Offset(1, 0).text = "" Then
X = X + 1
Else
Range("A" & X).Formula = Join(Application.Transpose(Range("A" & X & ":A" & X + 1)), "")
Range("A" & X + 1).Delete xlUp
End If
Loop
End Sub
I normally work backwards also but for this one went forwards.
I had memory processing in mind.
Sub merg()
Dim v As Long, w As Long, vVALs As Variant
With ActiveSheet 'reference the worksheet properly!
With .Range(.Cells(1, 1), .Cells(Rows.Count, 1).End(xlUp))
vVALs = .Cells.Value2
For v = LBound(vVALs, 1) To UBound(vVALs, 1)
If vVALs(v, 1) = vbNullString Then
For w = v + 1 To UBound(vVALs, 1)
If vVALs(w, 1) = vbNullString Then Exit For
vVALs(v, 1) = vVALs(v, 1) & vVALs(w, 1)
vVALs(w, 1) = vbNullString
Next w
End If
Next v
.Cells = vVALs
With .SpecialCells(xlCellTypeBlanks)
.Delete Shift:=xlUp
End With
End With
End With
End Sub