How to run a compare though multiple sheets? - excel

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

Related

Convert date list into date ranges [duplicate]

I have a data like this :
A049
A050
A051
A053
A054
A055
A056
A062
A064
A065
A066
And I want the output like :
As you can see, I want the ranges which are in consecutive order
I am trying some thing like this:
Private Sub CommandButton1_Click()
Set wb = ThisWorkbook
lastRow = wb.Sheets("Sheet1").Range("A" & wb.Sheets("Sheet1").Rows.Count).End(xlUp).Row
For i = 2 To lastRow
r = wb.Sheets("Sheet1").Range("A" & i).Value
If wb.Sheets("Sheet1").Range("A" & i).Value = wb.Sheets("Sheet1").Range("A" & i+1).Value
Next i
End Sub
But not helping me
Am feeling charitable so have tried some code which should work. It assumes your starting values are in A1 down and puts results in C1 down.
Sub x()
Dim v1, v2(), i As Long, j As Long
v1 = Range("A1", Range("A" & Rows.Count).End(xlUp)).Value
ReDim v2(1 To UBound(v1, 1), 1 To 2)
For i = LBound(v1, 1) To UBound(v1, 1)
j = j + 1
v2(j, 1) = v1(i, 1)
If i <> UBound(v1, 1) Then
Do While Val(Right(v1(i + 1, 1), 3)) = Val(Right(v1(i, 1), 3)) + 1
i = i + 1
If i = UBound(v1, 1) Then
v2(j, 2) = v1(i, 1)
Exit Do
End If
Loop
End If
If v1(i, 1) <> v2(j, 1) Then v2(j, 2) = v1(i, 1)
Next i
Range("C1").Resize(j, 2) = v2
End Sub
Try the below code
Private Sub CommandButton1_Click()
Set wb = ThisWorkbook
lastRow = wb.Sheets("Sheet1").Range("A" & wb.Sheets("Sheet1").Rows.Count).End(xlUp).Row
Dim lastNum, Binsert As Integer
Dim firstCell, lastCell, currentCell As String
Binsert = 1
lastNum = getNum(wb.Sheets("Sheet1").Range("A1").Value)
firstCell = wb.Sheets("Sheet1").Range("A1").Value
For i = 2 To lastRow
activeNum = getNum(wb.Sheets("Sheet1").Range("A" & i).Value)
currentCell = wb.Sheets("Sheet1").Range("A" & i).Value
If (activeNum - lastNum) = 1 Then
'nothing
Else
lastCell = wb.Sheets("Sheet1").Range("A" & (i - 1)).Value
wb.Sheets("Sheet1").Range("B" & Binsert).FormulaR1C1() = firstCell
If (firstCell <> lastCell) Then
wb.Sheets("Sheet1").Range("C" & Binsert).FormulaR1C1() = lastCell
End If
Binsert = Binsert + 1
firstCell = wb.Sheets("Sheet1").Range("A" & i).Value
End If
lastNum = activeNum
Next i
'last entry
wb.Sheets("Sheet1").Range("B" & Binsert).FormulaR1C1() = firstCell
If (firstCell <> currentCell) Then
wb.Sheets("Sheet1").Range("C" & Binsert).FormulaR1C1() = currentCell
End If
End Sub
Public Function getNum(ByVal num As String) As Integer
getNum = Val(Mid(num, 2))
End Function
Another solution. It loops backwards from last row to first row.
Option Explicit
Public Sub FindConsecutiveValues()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim lRow As Long 'find last row
lRow = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
Dim lVal As String 'remember last value (stop value)
lVal = ws.Range("A" & lRow).Value
Const fRow As Long = 2 'define first data row
Dim i As Long
For i = lRow To fRow Step -1 'loop from last row to first row backwards
Dim iVal As Long
iVal = Val(Right(ws.Range("A" & i).Value, Len(ws.Range("A" & i).Value) - 1)) 'get value of row i without A so we can calculate
Dim bVal As Long
bVal = 0 'reset value
If i <> fRow Then 'if we are on the first row there is no value before
bVal = Val(Right(ws.Range("A" & i - 1).Value, Len(ws.Range("A" & i - 1).Value) - 1)) 'get value of row i-1 without A
End If
If iVal - 1 = bVal Then
ws.Rows(i).Delete 'delete current row
Else
If lVal <> ws.Range("A" & i).Value Then 'if start and stop value are not the same …
ws.Range("B" & i).Value = lVal 'write stop value in column B
End If
lVal = ws.Range("A" & i - 1).Value 'remember now stop value
End If
Next i
End Sub

Match rows for value in specific columns and paste matched/unmatched rows in new sheet

I got data in sheet1 and sheet2, which i want to copy and paste in sheet3. That is already done. So next i want to match rows, by checking column C, D, E, H and I. The C and H column value is integer and the rest is text/strings.
If two rows match, then i want to copy and paste one of the lines in a new third sheet, and add the integer difference from column H in column H (The difference will be 0 if the lines match in all columns)
If the two rows dont match, copy and paste one of the lines in a new fourth sheet, and add the integer difference from column H in column H
The code so far:
Sub CopyPasteSheet()
Dim mySheet, arr
arr = Array("Sheet1", "Sheet2")
Const targetSheet = "Sheet3"
Application.ScreenUpdating = False
For Each mySheet In arr
Sheets(mySheet).Range("A1").CurrentRegion.Copy
With Sheets(targetSheet)
.Range("A1").Insert Shift:=xlDown
If mySheet <> arr(UBound(arr)) Then .Rows(1).Delete xlUp
End With
Next mySheet
Application.ScreenUpdating = True
End Sub
Code so far, but i receive a code error "Application-defined or object-defined error". It does copy the rows which match into a new sheet and state the difference as 0 in column H, but it doesn't work for the ones that dont match.
Sub MatchRows()
Dim a As Variant, b As Variant, c As Variant, d As Variant
Dim i As Long, j As Long, k As Long, m As Long, n As Long
Dim dic As Object, ky As String
Set dic = CreateObject("Scripting.Dictionary")
a = Sheets("Sheet1").Range("A1:I" & Sheets("Sheet1").Range("H" & Rows.Count).End(3).Row).Value
b = Sheets("Sheet2").Range("A1:I" & Sheets("Sheet2").Range("H" & Rows.Count).End(3).Row).Value
ReDim c(1 To UBound(a, 1), 1 To UBound(a, 2))
ReDim d(1 To UBound(a, 1), 1 To UBound(a, 2))
For i = 1 To UBound(b, 1)
ky = b(i, 3) & "|" & b(i, 4) & "|" & b(i, 5) & "|" & b(i, 9)
dic(ky) = i
Next
For i = 2 To UBound(a, 1)
ky = a(i, 3) & "|" & a(i, 4) & "|" & a(i, 5) & "|" & a(i, 9)
If dic.exists(ky) Then
j = dic(ky)
If a(i, 8) = b(j, 8) Then
k = k + 1
For n = 1 To UBound(a, 2)
c(k, n) = a(i, n)
Next
c(k, 8) = 0
Else
m = m + 1
For n = 1 To UBound(a, 2)
d(k, n) = a(i, n)
Next
d(k, 8) = a(i, 8) - b(j, 8)
End If
End If
Next
Sheets("Sheet3").Range("A" & Rows.Count).End(3)(2).Resize(k, UBound(a, 2)).Value = c
Sheets("Sheet4").Range("A" & Rows.Count).End(3)(2).Resize(m, UBound(a, 2)).Value = d
End Sub
Unless the performance is too slow remove the complexity of arrays by writing to the output sheets one line at a time.
Update - copy complete line
Option Explicit
Sub MatchRows2()
Dim dic As Object, key As String
Set dic = CreateObject("Scripting.Dictionary")
Dim wb As Workbook
Dim ws As Worksheet, ws3 As Worksheet, ws4 As Worksheet
Dim iLastRow As Long, s As String, diff As Long
Dim iRow3 As Long, iRow4 As Long, i As Long, t0 As Single
Dim rng As Range
t0 = Timer
s = "|"
Set wb = ThisWorkbook
' sheet 2
Set ws = wb.Sheets("Sheet2")
iLastRow = ws.Cells(Rows.Count, "H").End(xlUp).Row
For i = 2 To iLastRow
key = ws.Cells(i, "C") & s & ws.Cells(i, "D") _
& s & ws.Cells(i, "E") & s & ws.Cells(i, "I")
If dic.exists(key) Then
MsgBox "Duplicate key '" & key & "'", vbCritical, "Sheet2 Row " & i
Exit Sub
Else
dic.Add key, ws.Cells(i, "H")
End If
Next
Debug.Print dic.Count
' results
Set ws3 = wb.Sheets("Sheet3")
iRow3 = ws3.Cells(Rows.Count, "A").End(xlUp).Row
Set ws4 = wb.Sheets("Sheet4")
iRow4 = ws4.Cells(Rows.Count, "A").End(xlUp).Row
'sheet 1
Application.ScreenUpdating = False
Set ws = wb.Sheets("Sheet1")
iLastRow = ws.Cells(Rows.Count, "H").End(xlUp).Row
For i = 2 To iLastRow
key = ws.Cells(i, "C") & s & ws.Cells(i, "D") _
& s & ws.Cells(i, "E") & s & ws.Cells(i, "I")
If dic.exists(key) Then
diff = ws.Cells(i, "H") - dic(key)
If diff = 0 Then
iRow3 = iRow3 + 1
Set rng = ws3.Cells(iRow3, "A")
Else
iRow4 = iRow4 + 1
Set rng = ws4.Cells(iRow4, "A")
End If
ws.Rows(i).Copy rng
rng.Offset(0, 7).Value = diff ' col H
End If
Next
Application.ScreenUpdating = True
MsgBox "Done in " & Format(Timer - t0, "0.0 secs"), vbInformation
End Sub

VBA delete rows that offset eachother

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

Auto filter with sheet name and Delete Hidden rows

i want to Delete data from excel based on a filter. filter data based on sheet name with specified column ex col A, and delete Hidden value.
my intention to keep the data based on the sheet name, i have tried with loop but it take long time for processing.
wb.Activate
wb.Sheets("PMCC 1").Select
For Each ws In wb.Worksheets
ws.Activate
index = index + 1
If index <= 10 Then
irow = ws.Cells(Rows.Count, 1).End(xlUp).Row
newtemp = Replace(ws.Name, " ", "#0")
For J = irow To 2 Step -1
If ws.Cells(J, 1) <> newtemp Then
ws.Cells(J, 1).EntireRow.Delete
End If
Next J
Else
irow = ws.Cells(Rows.Count, 1).End(xlUp).Row
newtemp = Replace(ws.Name, " ", "#")
For J = irow To 2 Step -1
If ws.Cells(J, 1) <> newtemp Then
ws.Cells(J, 1).EntireRow.Delete
End If
Next J
End If
Next ws
MsgBox ("Deleted")
You can modify & try this solution:
Option Explicit
Sub test()
Dim ws As Worksheet
Dim LastRow As Long, Index As Long, j As Long
Dim newtemp As String
For Each ws In ThisWorkbook.Worksheets
Index = Index + 1
With ws
If Index <= 10 Then
newtemp = Replace(.Name, " ", "#0")
Else
newtemp = Replace(.Name, " ", "#")
End If
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
For j = LastRow To 2 Step -1
If .Cells(j, 1) <> newtemp Then
.Rows(j).EntireRow.Delete
End If
Next j
End With
Next ws
MsgBox ("Deleted")
End Sub

How to unmerge rows and not column

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

Resources