How do you fix VBA code that counts 1 too many? - excel

I have written a program that counts bins that are empty (verified), empty (unverified), and not accessible (bins locked).
I am trying to count the bins that are locked from my Bin Conversions sheet that if they are TRUE (there are 20 that are true), then they are locked and will be counted on my Bin Report sheet.
My Bin Reports sheet counts 1 too many for each group (all groups total 23 instead of 20). A group example would be 4-Pallet, 2.5ft, 2 bins locked (instead of 1).
Bin Report
Bin Conversions
Sub getBinStatusArray()
calc (False)
Dim dSH As Worksheet
Dim brSH As Worksheet
Dim bcSH As Worksheet
Set dSH = ThisWorkbook.Sheets("data")
Set brSH = ThisWorkbook.Sheets("Bin Report")
Set bcSH = ThisWorkbook.Sheets("Bin Conversions")
Dim binLockCell As Byte, binType As String, binSize As Variant, binLocked As Boolean, b As Long, i As Long
Dim dataArray() As Variant
Dim binIDArray As Variant
'Create empty array cells
ReDim Preserve dataArray(1 To dSH.Range("A" & Rows.Count).End(xlUp).Row, 1 To 3)
'Navigates cells
With dSH
lastrow = .Cells(Rows.Count, 1).End(xlUp).Row
dataArray = .Range(.Cells(lastrow, 1), .Cells(1,
.Columns.Count).End(xlToLeft)).Value
End With
'Count Bin Conversion Cells
With bcSH
lastrow = .Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To lastrow
.Range("E" & i).Value2 = Application.WorksheetFunction.CountIf(dSH.Range("A:A"), .Range("A" & i).Value2)
Next i
End With
'Generate Bin Report
With brSH
.Cells.ClearContents
.Range("H1").Value = "Filter Input"
.Range("B1").Value = "Bin Type"
.Range("I1").Value = "Bin Type"
.Range("C1").Value = "Bin Height"
.Range("J1").Value = "Bin Height"
.Range("D1").Value = "Verified"
.Range("K1").Value = "Verified"
.Range("E1").Value = "Unverified"
.Range("L1").Value = "Unverified"
.Range("F1").Value = "Bins Locked"
.Range("M1").Value = "Bins Locked"
For i = 2 To lastrow
If bcSH.Range("E" & i).Value = 1 Or Application.WorksheetFunction.CountIfs(bcSH.Range("G" & i), "true") Then
binType = bcSH.Range("B" & i).Value
binSize = bcSH.Range("C" & i).Value
binLocked = bcSH.Range("H" & i).Value
If .Range("b2") = "" Then
.Range("b2").Value = bcSH.Range("B" & i).Value
.Range("c2").Value = bcSH.Range("C" & i).Value
.Range("F2").Value2 = Application.WorksheetFunction.CountIfs(bcSH.Range("G" & i), "true")
ElseIf .Range("b2") <> "" Then
lastrow = brSH.Cells(Rows.Count, 2).End(xlUp).Row
For b = 2 To lastrow + 1
If brSH.Range("B" & b) = binType And brSH.Range("C" & b) = binSize Then
brSH.Range("D" & b) = brSH.Range("D" & b) + bcSH.Range("E" & i)
binLockCell = Application.WorksheetFunction.CountIfs(bcSH.Range("G" & i), "true")
brSH.Range("F" & b) = binLockCell + brSH.Range("F" & b)
Exit For
ElseIf b = lastrow Then
.Range("b" & b + 1).Value = bcSH.Range("B" & i).Value
.Range("c" & b + 1).Value = bcSH.Range("c" & i).Value
.Range("D" & b + 1).Value = bcSH.Range("E" & i).Value
binLockCell = Application.WorksheetFunction.CountIfs(bcSH.Range("G" & i), "true")
.Range("F" & b + 1) = binLockCell + .Range("F" & b + 1)
End If
Next b
End If
End If
Next i
Range("b1").CurrentRegion.sort key1:=Range("b1"), order1:=xlAscending, _
key2:=Range("C1"), order2:=xlAscending, Header:=xlYes
End With
calc (True)
End Sub

You are looping For b = 2 To lastrow + 1 but adding a new line when b = lastrow i.e. before the loop has ended. So on the last iteration when b = lastrow + 1 it summates the record again. One fix would be use a flag.
ElseIf .Range("b2") <> "" Then
Dim bExists: bExists = False
lastrow = brSH.Cells(Rows.Count, 2).End(xlUp).Row
' increment existing
For b = 2 To lastrow
If brSH.Range("B" & b) = binType And brSH.Range("C" & b) = binSize Then
brSH.Range("D" & b) = brSH.Range("D" & b) + bcSH.Range("E" & i)
binLockCell = Application.WorksheetFunction.CountIfs(bcSH.Range("G" & i), "true")
brSH.Range("F" & b) = binLockCell + brSH.Range("F" & b)
bExists = True
Exit For
Next b
' or add new line
If Not bExists Then
.Range("b" & b + 1).Value = bcSH.Range("B" & i).Value
.Range("c" & b + 1).Value = bcSH.Range("c" & i).Value
.Range("D" & b + 1).Value = bcSH.Range("E" & i).Value
binLockCell = Application.WorksheetFunction.CountIfs(bcSH.Range("G" & i), "true")
.Range("F" & b + 1) = binLockCell + .Range("F" & b + 1)
End If
End If

Related

How to use vba vlookup formula from two sheets?

I have two sheets first one called Sheet8 is for the Main table that can be used by the Data Entry Form to enter the data in that table, and the second called Sheet9 that includes the table for the vlookup. What I want is in the data entry user form as soon as I enter the Name the Discipline is created automatically based on that name.
Screenshot of the Data Entry for the Main table in sheet8
Screenshot of the sheet9 Table
The code for Save Button
Private Sub btnSave_Click()
Dim sh As Worksheet
Set sh = ThisWorkbook.Sheets("Sheet8")
Dim n As Long
n = sh.Range("A" & Application.Rows.Count).End(xlUp).Row
sh.Unprotect "1234"
sh.Range("A" & n + 1).Value = Me.txtDate.Value
sh.Range("B" & n + 1).Value = Me.txtName.Value
sh.Range("C" & n + 1).Value = Me.txtProjNo.Value
sh.Range("D" & n + 1).Value = Me.txtProjTitle.Value
sh.Range("E" & n + 1).Value = Me.txtBVEntity.Value
sh.Range("F" & n + 1).Value = Me.txtZIG.Value
sh.Range("G" & n + 1).Value = Me.txtSpenthrs.Value
sh.Range("H" & n + 1).Value = Me.comboCategory.Value
sh.Range("I" & n + 1).Value = Me.txtDiscipline.Value
sh.Range("J" & n + 1).Value = Me.txtSCV.Value
sh.Range("K" & n + 1).Value = Me.txtTotSCV.Value
sh.Range("L" & n + 1).Value = Me.txtCotMER.Value
sh.Range("M" & n + 1).Value = Me.txtBudgethrs.Value
sh.Range("N" & n + 1).Value = Me.txtBudget.Value
sh.Range("O" & n + 1).Value = Me.txtProgress.Value
sh.Range("P" & n + 1).Value = Me.txtEndDate.Value
sh.Protect "1234"
The code for the Name textbox
Private Sub txtName_AfterUpdate()
If WorksheetFunction.CountIf(Sheet9.Range("C:D"), Me.txtName.Value) = 0 Then
MsgBox "This Name is Invalid."
Me.txtName.Value = ""
Exit Sub
End If
With Me
.txtDiscipline = Application.WorksheetFunction.VLookup(Me.txtName, Sheet9.Range("Lookup"), 4, 0)
End With
End Sub

Checking each row and adding or updating it accordingly in a new sheet

I am trying to copy from Raw Data to Consolidated Data.
If a row in the Raw Data already exists (by checking both the Full Name and the Identity Number together with the AND Condition as the composite key of using both values is unique) in the Consolidated Data it will check through the columns and update it if there are any changes.
For example Ben with the Identity Number of 3333 changed his Contact and Email. So after updating it will have the updated Contact and Email.
Secondly, if the row in the Raw Data doesn't exist it will add to the bottom of the Consolidated Data.
I tried a nested loop for this. I am facing the problem of duplicate entries as my code is checking the row in the Raw Data with every row in the Consolidated Data.
For example in the Raw Data I have Alan (1111), Ben (2222), Ben (3333), Calvin (4444). In the New Consolidated Data I will turn out to have more than four rows after updating.
This is the raw data
This is the old consolidated data
This is the final consolidated data
Dim i As Long, lastRow As Long
Set rawData = sheet1
Set finalData = sheet2
lastRow = sheet1.Cells(rows.Count, "A").End(xlUp).Row
finalLastRow = sheet2.Cells(rows.Count, "A").End(xlUp).Row
MsgBox lastRow
MsgBox finalLastRow
' If the sheet is empty
If (finalLastRow = 2) Then
For i = 3 To lastRow
' Test if cell if empty
If (rawData.range("A" & i).Value <> "") Then
finalData.range("A" & i).Value = rawData.range("A" & i).Value
finalData.rNeange("B" & i).Value = rawData.range("B" & i).Value
finalData.range("C" & i).Value = rawData.range("C" & i).Value
finalData.range("D" & i).Value = rawData.range("D" & i).Value
finalData.range("E" & i).Value = rawData.range("E" & i).Value
End If
Next i
' If the sheet is not empty
ElseIf (finalLastRow <> 2) Then
Dim newLastRow As Long
newLastRow = 4
For i = 3 To lastRow
For j = 3 To finalLastRow
' Test if cell is the same
'Dim matchScore As Long
' Full Name and Identity Number remain same
If (rawData.range("A" & i).Value) = (finalData.range("A" & j).Value) And (rawData.range("B" & i).Value) = (finalData.range("B" & j).Value) Then
finalData.range("C" & j).Value = rawData.range("C" & i).Value
finalData.range("D" & j).Value = rawData.range("D" & i).Value
finalData.range("E" & j).Value = rawData.range("E" & i).Value
MsgBox "SAME"
' New Record
Else
' Check through the sheet2 to see if the row have been added to it
newLastRow = newLastRow + 1
finalData.range("B" & newLastRow).Value = rawData.range("B" & i).Value
End If
finalLastRow = sheet2.Cells(rows.Count, "A").End(xlUp).Row
Next j
Next i
End If
I have added another For loop in the Else statement to check through the columns to see if it exists.
' New Record
Else
Dim isDataInCurrentRows As Boolean
isDataInCurrentRows = False
For checkRow = 3 To finalLastRow
If (rawData.range("B" & i).Value) = (finalData.range("B" & checkRow).Value) And (rawData.range("D" & i).Value) = (finalData.range("D" & checkRow).Value) Then
isDataInCurrentRows = True
Exit For
End If
Next checkRow
If isDataInCurrentRows = False Then
finalData.range("A" & finalLastRow + 1).Value = rawData.range("A" & i).Value
finalData.range("B" & finalLastRow + 1).Value = rawData.range("B" & i).Value
finalData.range("C" & finalLastRow + 1).Value = rawData.range("C" & i).Value
finalData.range("D" & finalLastRow + 1).Value = rawData.range("D" & i).Value
finalData.range("E" & finalLastRow + 1).Value = rawData.range("E" & i).Value
finalData.range("F" & finalLastRow + 1).Value = rawData.range("F" & i).Value
finalData.range("G" & finalLastRow + 1).Value = rawData.range("G" & i).Value
finalData.range("H" & finalLastRow + 1).Value = rawData.range("H" & i).Value
finalData.range("I" & finalLastRow + 1).Value = Date
End If
End If

A better way to union two excel tables

I have two tables
A B C
name surname address
and
A B C D
id address name surname
I need to union the tables and matching the cols, so
table1, colA = table2, colC
table1, colC = table2, colB
etc
I use this code, which works fine, but for big data is slow
Sub unionrep()
Dim lastRow As Long
Sheets("decl").Select
With ActiveSheet
lastRow = .Cells(.Rows.Count, "b").End(xlUp).Row
End With
With Sheets("onl")
tlastRow = .Cells(.Rows.Count, "b").End(xlUp).Row
End With
For i = 1 To lastRow
Sheets("onl").Range("a" & tlastRow + i + 1).Value = Range("a" & i).Value
Sheets("onl").Range("b" & tlastRow + i + 1).Value = Trim(Range("b" & i).Value)
Sheets("onl").Range("c" & tlastRow + i + 1).Value = "*" & Range("c" & i).Value
Sheets("onl").Range("d" & tlastRow + i + 1).Value = Range("g" & i).Value
Sheets("onl").Range("e" & tlastRow + i + 1).Value = Range("d" & i).Value
Sheets("onl").Range("f" & tlastRow + i + 1).Value = ""
Sheets("onl").Range("g" & tlastRow + i + 1).Value = ""
Sheets("onl").Range("h" & tlastRow + i + 1).Value = ""
Sheets("onl").Range("i" & tlastRow + i + 1).Value = Range("e" & i).Value
Sheets("onl").Range("j" & tlastRow + i + 1).Value = Range("i" & i).Value
Sheets("onl").Range("k" & tlastRow + i + 1).Value = Range("f" & i).Value
Next
Sheets("onl").Select
End Sub
You could copy and paste the entire ranges instead of looping through the rows. For example, to copy from column A in the "decl" sheet to column C in the "onl" sheet, something like:
Sheets("decl").Range(Cells(1, 1), Cells(lastRow, 1)).Copy
Sheets("onl").Range("C" & tlastRow + 1).PasteSpecial
Try using arrays:
Sub unionrep()
Dim lastRow As Long
Dim vDataIn, vDataOut
With Sheets("decl")
lastRow = .Cells(.Rows.Count, "b").End(xlUp).Row
vDataIn = .Range("A1:I" & lastRow).Value
End With
ReDim vDataOut(1 To lastRow, 1 To 11)
With Sheets("onl")
tlastRow = .Cells(.Rows.Count, "b").End(xlUp).Row + 1
End With
For i = 1 To lastRow
vDataOut(i, 1) = vDataIn(i, 1)
vDataOut(i, 2) = Trim(vDataIn(i, 2))
vDataOut(i, 3) = "*" & vDataIn(i, 3)
vDataOut(i, 4) = vDataIn(i, 7)
vDataOut(i, 5) = vDataIn(i, 4)
vDataOut(i, 9) = vDataIn(i, 5)
vDataOut(i, 10) = vDataIn(i, 9)
vDataOut(i, 11) = vDataIn(i, 6)
Next
Sheets("onl").Range("a" & tlastRow).Resize(UBound(vDataOut, 1), UBound(vDataOut, 2)).Value = vDataOut
Sheets("onl").Select
End Sub

Excel VBA Paste error when applying style to cell

I'm seeking some assistance. I have a code that does what I need and works pretty fine, but I want to make it do some more, and thats when it breaks.
Here is the code, a bit messy I know:
Sub AgainstAbstain()
Application.ScreenUpdating = False
'Stating variables
Dim Abstain As String
Abstain = "Abstain"
Dim Against As String
Against = "Against"
Dim C11 As Variant
'Enter amount of votable items
Dim e As Byte 'number of agenda items
e = InputBox("Number of votable items in Agenda?")
'Create Necessary sheets
On Error Resume Next
Sheets("Abstain").Delete
'Sheets("Against").Delete
On Error GoTo 0
ActiveWorkbook.Sheets.Add After:=Worksheets(Worksheets.Count)
'ActiveWorkbook.Sheets.Add After:=Worksheets(Worksheets.Count)
ActiveWorkbook.Sheets(2).Name = "Abstain"
'ActiveWorkbook.Sheets(3).Name = "Against"
'Change zoom level of sheets
Sheets(2).Activate
ActiveWindow.Zoom = 85
'Sheets(3).Activate
'ActiveWindow.Zoom = 85
Sheets(1).Activate
'For better copying of cells
Cells.WrapText = False
'To count spaces
Dim j As Integer
j = 1
Dim k As Integer
k = 1
Dim c As Integer
c = 3 '
'Main filter and copy
For i = 1 To e
Worksheets(1).Cells(11, c).Select
C11 = ActiveCell.Value
'Range("A11:C11").Select
Range(Cells(11, 1), Cells(11, c)).Select
Selection.AutoFilter
ActiveSheet.Range("C:C").AutoFilter Field:=c, Criteria1:="ABSTAIN"
'Amount of items visible after filter
Dim x As Integer
x = Application.Subtotal(3, Columns("A")) - 19
'MsgBox x
If x > 0 Then
ActiveSheet.AutoFilter.Range.Offset(1).Copy
Sheets("ABSTAIN").Activate
' Range("A" & j).Select
' Range("A" & j).Font.Bold = True
' Range("A" & j).Font.Underline = True
Range("A" & j).Value = C11 & ") " & Abstain
j = j + 2
' Range("A" & j).Select
Range("A" & j).Value = "Beneficial owner:"
'Range("A" & j).Font.Bold = True
Range("B" & j).Value = "Number of shares:"
'Range("A" & j).Font.Bold = True
j = j + 1
Sheets(2).Range("A" & j).PasteSpecial
' Range("A" & j).Select
' ActiveSheet.Paste
j = j + x
Range("A" & j).Value = "Sum"
Range("A" & j).Font.Bold = True
Range("A" & j).Interior.Color = RGB(255, 204, 153)
Range("B" & j).Font.Bold = True
Range("B" & j).Interior.Color = RGB(255, 204, 153)
j = j + 3
Columns(3).EntireColumn.Delete
Err.Clear
Sheets(1).Activate
Worksheets(1).Columns(c).Hidden = True
c = c + 1
Cells.AutoFilter
Else: Cells.AutoFilter
Worksheets(1).Columns(c).Hidden = True
c = c + 1
End If
Next i
Cells.EntireColumn.Hidden = False
c = 3
For i = 1 To e
Worksheets(1).Cells(11, c).Select
C11 = ActiveCell.Value
'Range("A11:C11").Select
Range(Cells(11, 1), Cells(11, c)).Select
Selection.AutoFilter
ActiveSheet.Range("C:C").AutoFilter Field:=c, Criteria1:="AGAINST"
'Amount of items visible after filter
Dim y As Integer
y = Application.Subtotal(3, Columns("A")) - 19
'MsgBox y
If y > 0 Then
ActiveSheet.AutoFilter.Range.Offset(1).Copy
Sheets("Abstain").Activate
' Range("A" & j).Select
Range("A" & j).Value = C11 & ") " & Abstain
j = j + 2
' Range("A" & j).Select
Range("A" & j).Value = "Beneficial owner:"
Range("B" & j).Value = "Number of shares:"
j = j + 1
Sheets(2).Range("A" & j).PasteSpecial
' Range("A" & j).Select
' ActiveSheet.Paste
j = j + y
Range("A" & j).Value = "Sum"
Range("A" & j).Font.Bold = True
Range("A" & j).Interior.Color = RGB(255, 153, 204)
Range("B" & j).Font.Bold = True
Range("B" & j).Interior.Color = RGB(255, 153, 204)
j = j + 3
Columns(3).EntireColumn.Delete
Err.Clear
Sheets(1).Activate
Worksheets(1).Columns(c).Hidden = True
c = c + 1
Cells.AutoFilter
Else: Cells.AutoFilter
Worksheets(1).Columns(c).Hidden = True
c = c + 1
End If
'If y > 0 Then
'ActiveSheet.AutoFilter.Range.Offset(1).Copy
' Sheets("AGAINST").Activate
' Range("A" & k).Select
' Range("A" & k).Value = C11 & ") " & Against
' k = k + 2
' Range("A" & k).Select
' Range("A" & k).Value = "Beneficial owner:"
' k = k + 1
' Range("A" & k).Select
' ActiveSheet.Paste
' k = k + y
' Range("A" & k).Value = "Sum"
' k = k + 3
' Columns(3).EntireColumn.Delete
' Err.Clear
' Sheets(1).Activate
' Cells.AutoFilter
' 'Columns(3).EntireColumn.Delete
' Worksheets(1).Columns(c).Hidden = True
' c = c + 1
'Else: Cells.AutoFilter
' 'Columns(3).EntireColumn.Delete
' Worksheets(1).Columns(c).Hidden = True
' c = c + 1
'End If
Next i
Sheets(2).Activate
For Each NumRange In Columns("B").SpecialCells(xlConstants, xlNumbers).Areas
SumAddr = NumRange.Address(False, False)
NumRange.Offset(NumRange.Count, 0).Resize(1, 1).Formula = "=SUM(" & SumAddr & ")"
c = NumRange.Count
Next NumRange
NoData:
'Sheets(2).Select
Columns("A:B").AutoFit
Sheets(1).Activate
Cells.EntireColumn.Hidden = False
Application.ScreenUpdating = True
End Sub
It filters and moves data just fine. But when i try to activate this part
' Range("A" & j).Font.Bold = True
' Range("A" & j).Font.Underline = True
It gives me this error
Run-time error '1004':
PasteSpecial method of Range class failed. In fact, if I try to activate any style change before the paste i get this error.
And highlights this area
Sheets(2).Range("A" & j).PasteSpecial
I just don't get.
After the .Copy method you need to immediately paste the results. Doing anything else will empty the copy buffer, so this will work:
ActiveSheet.Range("A1").Copy
ActiveSheet.Range("A2").PasteSpecial
ActiveSheet.Range("A1").Font.Size = 10
But this won't
ActiveSheet.Range("A1").Copy
ActiveSheet.Range("A1").Font.Size = 10
ActiveSheet.Range("A2").PasteSpecial

Check duplicates copy in one cell

I have this code to check duplicates, If it find duplicates (or more) in cell L, is it possible to copy the values from cells in the K column into ONE cell?
Sub check_duplicates()
Dim x As Long
Dim LastRow As Long
Dim rng As String
LastRow = Range("L65536").End(xlUp).Row
For x = LastRow To 1 Step -1
If Application.WorksheetFunction.CountIf(Range("L2:L" & x), Range("L" & x).Value) > 1 Then
Range("L" & x).Copy
End If
Next x
End Sub
I hope is what you want to do, let me know.
Sub Test()
Dim lastrow As Long
lastrow = Range("L" & Rows.Count).End(xlUp).Row
For i = 2 To lastrow
lastrow = Range("L" & Rows.Count).End(xlUp).Row
For j = i + 1 To lastrow
If Range("L" & j).Value = Range("L" & i).Value Then
If Not IsEmpty(Range("K" & i)) Then
Range("K" & i) = Range("K" & i) & "," & " " & Range("L" & j)
Rows(j).EntireRow.Delete
Else
Range("K" & i) = Range("L" & j)
End If
j = j - 1
End If
Next j
Next i
End Sub

Resources