Add/Delete Rows based on cell value - excel

so I have an excel table with 2 columns: Times and Count, and 3 Rows: 1:00, 2:00 and 3:00 pm. I want functionality where when the user changes the count value for any of the rows, the count value minus 1 row should be added underneath. So for example for 1:00 pm below, when the user enters '4', it should add three rows below that row for a total of 4 rows. If the user changes the count to '2' it should remove 2 rows so that there are 2 total rows. This is what I have so far:
Times Count
1:00pm 4
2:00pm 0
3:00pm 0
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
Set KeyCells = Range("C5:C100")
If Not Application.Intersect(KeyCells, Range(Target.Address)) Is Nothing Then
'Save Workbook before so that original document will be saved
ActiveWorkbook.Save
Dim List As Long
Dim i As Long
Dim x As Long
Dim ExCnt As Variant
Dim aVal As Integer
'Find how many rows contain data
List = Range("B" & Rows.Count).End(xlUp).Row
For i = List To 2 Step -1
'Store exception value into variable
ExCnt = Range("C" & i).Value
With Range("C" & i)
'Insert rows unless text says Exception Count
If .Value > 1 And .Value <> "Exception Count" Then
.EntireRow.Copy
Application.EnableEvents = False
.Offset(1).EntireRow.Resize(.Value - 1).Insert
End If
CleanExit:
End With
Next i
Application.EnableEvents = True
End If
End Sub
This code adds the right amount of rows for each row but if the user changes the count value, the effect will compound for the existing rows.

I hope you appreciate how intricate this can actually be. :-)
Try this solution ...
Private Sub Worksheet_Change(ByVal Target As Range)
Dim strKey As String, lngCount As Long, lngOffset As Long
Dim i As Long, rngNewRows As Range, lngTopRow As Long, lngBottomRow As Long
Dim bInBetween As Boolean
' Anymore than 10 cells and we'll skip this process.
If Target.Cells.Count > 1 Then Exit Sub
If Target.Column = 2 Then
On Error Resume Next
Err.Clear
lngCount = Target.Value
On Error GoTo 0
If Err.Description = "" Then
If lngCount = 0 Then lngCount = 1
If lngCount > 0 Then
' Get the time value.
strKey = Target.Offset(0, -1).Text
bInBetween = False
' Check to make sure that the user isn't entering a value in between an already exploded set of rows.
If Target.Row > 1 Then
If Target.Offset(-1, -1).Text = strKey Then bInBetween = True
End If
If Not bInBetween Then
lngOffset = 0
' Now check each column below and delete or add rows depending on the count.
Do While True
lngOffset = lngOffset + 1
If Target.Offset(lngOffset, -1).Text <> strKey Then Exit Do
Loop
Application.EnableEvents = False
If lngOffset < lngCount Then
' We need to add rows.
Set rngNewRows = Target.Worksheet.Rows(Target.Offset(lngOffset, 0).Row & ":" & Target.Offset(lngOffset, 0).Offset(lngCount - lngOffset - 1, 0).Row)
lngTopRow = rngNewRows.Cells(1, 1).Row
lngBottomRow = rngNewRows.Cells(rngNewRows.Rows.Count, 1).Row
rngNewRows.Insert
For i = lngTopRow To lngBottomRow
Target.Worksheet.Cells(i, Target.Column - 1) = Target.Offset(0, -1).Value
Next
Else
If lngOffset <> lngCount Then
' We're over the count, determine the rows to delete.
Target.Worksheet.Rows(Target.Offset(lngCount, 0).Row & ":" & Target.Offset(lngOffset - 1, 0).Row).Delete
Else
' We have 1 row and that's all that's been asked for.
End If
End If
Application.EnableEvents = True
End If
End If
End If
End If
End Sub
... you clearly have some other rules that need to be applied but this should get you going. Check out the image below to see it in action.
A few points ...
It tries to cater for individuals entering values in column B within the exploded range and if they do that, it won't react to the value. Not sure if that's a requirement but I assumed it was.
0 will be treated as 1, so both 1, 0 and cleared will result in the resetting of the line.
Deletions happen at the bottom. So if the number goes from 10 to 3, it will delete the last set of rows to bring it back to 3.
It will only react to 1 cell at a time being changed. It reduced the complexity of the solution.
Outside of that, you're on your own. :-)

Related

Add or Delete Columns Based on Cell Value

I'm trying to add columns (or delete them if the number is reduced) between where "ID" and "Total" are based on the cell value in B1.
How could this be done automatically every time the cell is updated?
Code I have so far
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
Set KeyCells = Range("B1")
If Not Application.Intersect(KeyCells, Range(Target.Address)) _
Is Nothing Then
Dim i As Integer
For i = 1 To Range("B1").Value
Columns("C:C").Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Next i
End If
End Sub
There are a number of issues in your code:
Unqualified range references refer to a default sheet object. While it won't be a problem in this instance (in a worksheet code behind module that object is sheet sheet containing the code, in any other module its the Activesheet), it's a bad habit to get into. Use the keyword Me to refer to the sheet the code is in.
When changing the sheet in a Worksheet_Change event, use Application.EnableEvents = False to prevent an event cascade (any time the code changes the sheet the event is called again)
Use an Error Handler to turn it back on (Application.EnableEvents = True)
Calculate how many columns to Insert or Delete based on existing columns
Range check the user input to ensure it's valid
Insert or delete in one block
On the assumption the "Totals" column contains a formula to sum the row (eg for 2 columns, row 4 it might be =Sum($C4:$D4), when you insert columns at column C the formula won't include the new columns. The code can update the formulas if required.
Target is already a range. No need to get its address as a string, then turn it back into a range, use it directly
Your code, refactored:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
Dim NumColumnsRequired As Long
Dim NumExistingColumns As Long
Dim NumToInsertOrDelete As Long
Dim TotalsRange As Range
On Error GoTo EH
Set KeyCells = Me.Range("B1")
If Not Application.Intersect(KeyCells, Target) Is Nothing Then
' Validate Entry
If Not IsNumeric(KeyCells.Value) Then Exit Sub
NumColumnsRequired = KeyCells.Value
If NumColumnsRequired <= 0 Or NumColumnsRequired > 16380 Then Exit Sub
Application.EnableEvents = False
NumExistingColumns = Me.Cells(3, Me.Columns.Count).End(xlToLeft).Column - 3
NumToInsertOrDelete = NumColumnsRequired - NumExistingColumns
Select Case NumToInsertOrDelete
Case Is < 0
' Delete columns
Me.Columns(3).Resize(, -NumToInsertOrDelete).Delete
Case Is > 0
' Insert columns
Me.Columns(3).Resize(, NumToInsertOrDelete).Insert CopyOrigin:=xlFormatFromLeftOrAbove
'Optional: update Total Formulas
Set TotalsRange = Me.Cells(Me.Rows.Count, Me.Cells(3, Me.Columns.Count).End(xlToLeft).Column).End(xlUp)
If TotalsRange.Row > 3 Then
Set TotalsRange = Me.Range(TotalsRange, Me.Cells(4, TotalsRange.Column))
TotalsRange.Formula2R1C1 = "=Sum(RC3:RC" & TotalsRange.Column - 1 & ")"
End If
Case 0
' No Change
End Select
End If
EH:
Application.EnableEvents = True
End Sub
may try the code below to have the result like
code is more or less self explanatory
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range, ColNum As Long, TotalCol As Long, LeftFixedCol As Long
Dim Rng As Range, c As Range
Set KeyCells = Range("B1")
If Application.Intersect(KeyCells, Target) Is Nothing Then Exit Sub
If IsNumeric(KeyCells.Value) = False Then Exit Sub
ColNum = KeyCells.Value
If ColNum <= 0 Then Exit Sub
Set Rng = Range(Cells(3, 1), Cells(3, Columns.Count))
Set c = Rng.Find("Total") 'the find is case senseticve, Change "Total" to desired key word to find
If c Is Nothing Then Exit Sub
TotalCol = c.Column
LeftFixedCol = 2 'Column A & B for Company and ID
Dim i As Integer
If TotalCol < LeftFixedCol + ColNum + 1 Then ' Add column
For i = TotalCol To LeftFixedCol + ColNum
Columns(i).Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Cells(3, i).Value = "Column " & i - LeftFixedCol ' may not use this line
Next i
End If
If TotalCol > LeftFixedCol + ColNum + 1 Then ' Add column
For i = TotalCol - 1 To LeftFixedCol + ColNum + 1 Step -1
Columns(i).Delete
Next i
End If
End Sub
However to keep the Sum formula on total Column consistence with added column, may limit number of minimum columns to 2 and inserting columns in between existing columns, by changing following
If ColNum <= 1 Then Exit Sub
and
Columns(i - 1).Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
also delete line inserting column heading
Cells(3, i).Value = "Column " & i - LeftFixedCol ' may not use this line
otherwise may add VBA code to change formula of total column to requirement.
You can try the following.
the named ranges are defined:
"B1" -> "ColumnNumber"
"B3" -> "Header.ID"
"F3" -> "Header.Total" (but it changes as you add / remove columns)"
Private Sub Worksheet_Change(ByVal Target As Range)
Dim headerId As Range, headerTotal As Range, columnNumber As Range
Dim currentNumberOfColumns As Integer, targetNumberOfColumns As Integer
Dim columnsToAdd As Integer, columnsToRemove As Integer
Dim i As Integer
On Error GoTo error_catch
Application.EnableEvents = False
Set columnNumber = Me.Range("ColumnNumber")
If Not Application.Intersect(columnNumber, Target) Is Nothing Then
Set headerId = Me.Range("Header.ID")
Set headerTotal = Me.Range("Header.Total")
targetNumberOfColumns = columnNumber.Value
If targetNumberOfColumns <= 0 Then
Application.EnableEvents = True
Exit Sub
End If
currentNumberOfColumns = headerTotal.Column - headerId.Column - 1
Debug.Print "Currently there are " & currentNumberOfColumns & " columns"
If currentNumberOfColumns = targetNumberOfColumns Then
Application.EnableEvents = True
Exit Sub
Else
If targetNumberOfColumns > currentNumberOfColumns Then
columnsToAdd = targetNumberOfColumns - currentNumberOfColumns
Debug.Print "Need to add " & columnsToAdd & " columns"
For i = 1 To columnsToAdd
headerTotal.Offset(0, -1).EntireColumn.Select
Selection.Copy
headerTotal.EntireColumn.Select
Selection.Insert Shift:=xlToRight
Next i
Else
columnsToRemove = -(targetNumberOfColumns - currentNumberOfColumns)
Debug.Print "Need to remove " & columnsToRemove & " columns"
For i = 1 To columnsToRemove
headerTotal.Offset(0, -1).EntireColumn.Select
Selection.Delete Shift:=xlToLeft
Next i
End If
End If
End If
columnNumber.Select
Application.CutCopyMode = False
Application.EnableEvents = True
Exit Sub
error_catch:
MsgBox Err.Description
Application.EnableEvents = True
End Sub

assigning priority based on user dynamically changing values excel vba

I have list courses in cell b and their respective priorities in cell c from 1 to 49.
what I want is if a user changes any value of the priority column i.e. "C". then all other priority should be adjusted accordingly. logic can be seen in the attached sheet. the priority numbers should change dynamically as the user enters the value.
so in example one referring column L in the attached sheet.
if user change the no 4 priority to 8 then the rest will go one down .
similarly now we have got new nos list. so if any other number changes then it should adjust accordingly,keeping in mind the new list
sheet snapshot attached
Tried the below code but it always starts with the value 1 again. So the values are not adjusted based on new list.
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim myVal As Variant
Dim iCount As Long
Dim cell As Range
Dim myRange As Range
Set myRange = Worksheets("Sheet1").Range("C1:C49")
If Intersect(Target, Range("C1:C49")) Is Nothing Or Target.Cells.Count > 1 Then Exit Sub
Application.EnableEvents = False
myVal = Target.Value
iCount = 1
For Each cell In myRange
If Intersect(Target, cell) Is Nothing Then
If iCount = myVal Then
iCount = iCount + 1
End If
cell.Value = iCount
iCount = iCount + 1
End If
Next cell
Application.EnableEvents = True
End Sub
Edited to work when first row is any row
The following was generated ...
from this code ...
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ExtVal As Variant, InsVal As Variant
Dim iLoop As Long
Dim InsRow As Long, ExtRow As Long
Dim foundArr() As Boolean
Dim myRange As Range
' initial settings
Set myRange = Range(Range("A1"), Range("A" & Rows.Count).End(xlUp))
ReDim foundArr(1 To myRange.Rows.Count)
For iLoop = 1 To myRange.Rows.Count
foundArr(iLoop) = False
Next iLoop
If Intersect(Target, myRange) Is Nothing Or Target.Cells.Count > 1 Then Exit Sub
' calculate the extracted value - the user entered value
ExtVal = Target.Value
' calculate the inserted value - the number the user typed over
For iLoop = 1 To myRange.Rows.Count
foundArr(myRange.Cells(iLoop, 1).Value) = True
Next iLoop
For iLoop = 1 To myRange.Rows.Count
If Not foundArr(iLoop) Then
InsVal = iLoop
Exit For
End If
Next iLoop
' calculate the insertion row - the row the user typed in.
InsRow = CLng(Right(Target.Address, 1))
' calculate the extraction row - the original row of the number the user typed
ExtRow = 0
For iLoop = 1 To myRange.Rows.Count
If myRange.Cells(iLoop, 1).Value = ExtVal And myRange.Cells(iLoop, 1).Row <> InsRow Then
ExtRow = myRange.Cells(iLoop, 1).Row
Exit For
End If
Next iLoop
' do the swap / shuffle
Application.EnableEvents = False
For iLoop = myRange.Rows.Count To 1 Step -1
Debug.Print "Evaluating Row " & myRange.Cells(iLoop, 1).Row
If (myRange.Cells(iLoop, 1).Row <= ExtRow) Then
If myRange.Cells(iLoop, 1).Row > InsRow + 1 Then
myRange.Cells(iLoop, 1).Value = myRange.Cells(iLoop - 1, 1).Value
Else
If myRange.Cells(iLoop, 1).Row = InsRow + 1 Then
myRange.Cells(iLoop, 1).Value = InsVal
End If
End If
End If
Next iLoop
Application.EnableEvents = True
End Sub

Automatically adding a month-summery row at the end of each month?

I'm using an excel sheet to track my working hours. Each row represents a working day and has "date", "time in", "time out", "total working hours", and "daily salary".
I'd like the sheet to automatically create a month summery line every time I write an entry in the next month (i.e if a row with the date 13/3/16 is followed by a row with the date 2/4/16, the second row will be pushed down, and a row with the summery (i.e: total monthly hours, total monthly salary) will be created in between. it should look something like that:
Is that possible? if so, how do I do it?
Thank you for your input!
You should place this code in Sheet Code Module. It worked for me on data organized like your.
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ThisSheet As Worksheet
Dim NewRow As Long
Dim OldTotalRange As Range
Dim OldTotalRow As Long
Set ThisSheet = ActiveSheet
'If not single cell is changed, exit sub
If Target.Cells.Count = 1 Then
'Disable events for prevent recursion
Application.EnableEvents = False
If Target.Column = 1 And Target.Row <> 1 And Target.value <> "" Then
If IsDate(Target.value) And IsDate(Target.Offset(-1, 0).value) Then
If Month(Target.value) <> Month(Target.Offset(-1, 0).value) Then
With ThisSheet
NewRow = Target.Row
On Error Resume Next
Set OldTotalRange = .Columns(1).Find(What:="Total", After:=Target, SearchDirection:=xlPrevious)
OldTotalRow = OldTotalRange.Row
'It's for first 'Total' when there isn't 'totals' before.
If OldTotalRow = 0 Then
OldTotalRow = 1
End If
.Rows(NewRow).Insert
.Cells(NewRow, 1) = "Total"
.Cells(NewRow, 4).FormulaR1C1 = "=SUM(R[-" & NewRow - OldTotalRow - 1 & "]C:R[-1]C)"
.Cells(NewRow, 5).FormulaR1C1 = "=SUM(R[-" & NewRow - OldTotalRow - 1 & "]C:R[-1]C)"
'It's formatting, you can delete it or change
.Range(.Cells(NewRow, 1), .Cells(NewRow, 5)).Interior.Color = RGB(196, 215, 155)
.Range(.Cells(NewRow, 1), .Cells(NewRow, 5)).Font.Bold = True
End With
End If
End If
End If
Else
Exit Sub
End If
'Enable events
Application.EnableEvents = True
End Sub

EXCEL VBA Code to search cell for match to a list and delete if no match

(pic link below for this example): The data starts on row "A11", one block of data is A11 to A14, I need to search that range to see if it contains a member name from a list on sheet 2, for example Erik Christensen, if the list on sheet 2 doesnt have that name I need to delete rows A11 thru A14 and continue to the next block. The list on sheet 2 will have a varying amount of members to check so that needs to be taken into consideration. Once all the rows have been processed, I need to sorth them back to start at row A11.Please see pic and I will be extremely thankful for any help.
Sheet 1
For the below answer, I have made a few assumptions:
Your data will always start on row 11 of the first sheet in the
workbook.
The search term will always be found in the second row, below
Object:...
The data will always present in rows of 4, as shown in the picture,
with End: in the 4th row.
The list of valid names is in column A (beginning on A1) of the
second sheet in the workbook.
By "sorted back to start on row A11", I assume you mean that the
remaining blocks of data should start on row A11 and continue to the
end of the data, not that any actual sorting (i.e. by name) is
required.
This code will loop through all blocks of data (beginning with the last one, since we are deleting rows). If any of the names in column A of the second sheet appear in the block of data, that block is skipped. Otherwise, if no names appear, that block is deleted.
Sub SearchAndDeleteList()
Dim i As Long
Dim j As Long
Dim LRow As Long
Dim LListRow As Long
Dim BMatch As Boolean
'Find last instance of "End:" in
LRow = Sheets(1).Range("A:A").Find(what:="End*", searchdirection:=xlPrevious).Row
'Find last non-blank row in column A of second sheet
LListRow = Sheets(2).Range("A:A").Find(what:="*", searchdirection:=xlPrevious).Row
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.DisplayAlerts = False
Application.EnableEvents = False
If LRow >= 11 Then
'Make sure there are at least 11 rows of data
i = LRow
'MsgBox "First checkpoint: Last row of data is " & LRow 'Comment out this line
Do
BMatch = False
For j = 1 To LListRow
'Test this block to see if the value from j appears in the second row of data
If InStr(1, Sheets(1).Range("A" & i - 2).Value2, Sheets(2).Range("A" & j).Value2) > 0 Then
BMatch = True
Exit For
End If
Next j
'Application.StatusBar = "Match status for row " & i & ": " & BMatch
If Not BMatch Then
'Loop backwards to find the starting row (no lower than 11)
For j = i To 11 Step -1
If Sheets(1).Range("A" & j).Value2 Like "Object:*" Then Exit For
Next j
Sheets(1).Rows(j & ":" & i).Delete
i = j - 1
Else
'Find next block
If i > 11 Then
For j = i - 1 To 11 Step -1
If Sheets(1).Range("A" & j).Value2 Like "End:*" Then Exit For
Next j
i = j
Else
i = 10 'Force the loop to exit
End If
End If
'Application.StatusBar = "Moving to row " & i
Loop Until i < 11
'Loop back through and delete any blank rows
LRow = Sheets(1).Range("A:A").Find(what:="*", searchdirection:=xlPrevious).Row
'MsgBox "Second checkpoint: new last row of data is " & LRow
For i = LRow To 11 Step -1
If Sheets(1).Range("A" & i).Value2 = vbNullString Then Sheets(1).Rows(i).Delete
Next i
End If
'Application.StatusBar = False
Application.EnableEvents = True
Application.DisplayAlerts = True
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
thanks to Nick's cracking actual OP's needs, I hereby propose a solution that should be more maintainable and/or changeable as per Op's future needs
Option Explicit
Sub SearchAndDeleteList2()
Dim dataSht As Worksheet
Dim dataRng As Range, namesRng As Range, cell As Range, rangeToDelete As Range
Dim firstAddress As String
'------------------------------
' setting stuff - begin
Set dataSht = ThisWorkbook.Sheets("Sheet1Data") '<== change 'data' sheet as per your needs
With dataSht
Set dataRng = .Range("A11:A" & .Cells(.Rows.Count, 1).End(xlUp).row)
End With
If dataRng.Rows(1).row < 11 Then Exit Sub
With ThisWorkbook.Sheets("Sheet2Names") '<== change 'names' sheet as per your needs
Set namesRng = .Range("A1:A" & .Cells(.Rows.Count, 1).End(xlUp).row)
End With
Call ApplicationSet(False, False, xlCalculationManual, False)
' setting stuff - end
'------------------------------
'------------------------------
' core code - begin
Set cell = dataRng.Find("End:", LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext)
If Not cell Is Nothing Then
firstAddress = cell.Address
Do
If Not MyMatch(GetName(cell.Offset(-2)), namesRng) Then Call UpdateRngToDelete(rangeToDelete, dataSht.Rows(cell.row).Offset(-3).Resize(4))
Set cell = dataRng.FindNext(cell)
Loop While cell.Address <> firstAddress
rangeToDelete.Delete
End If
' core code - end
'------------------------------
Call ApplicationSet(True, True, xlCalculationAutomatic, True)
End Sub
Function GetName(cell As Range) As String
Dim iIni As Integer
Dim iEnd As Integer
iIni = InStr(cell.value, """") '<== the 'name' is always preceeded by '"' character
iEnd = InStr(cell.value, "\") '<== the 'name' is always follwed by '/' character
GetName = Mid(cell.value, iIni + 1, iEnd - iIni - 1)
End Function
Sub UpdateRngToDelete(baseRng As Range, toBeAddedRng As Range)
If baseRng Is Nothing Then
Set baseRng = toBeAddedRng
Else
Set baseRng = Union(baseRng, toBeAddedRng)
End If
End Sub
Function MyMatch(value As String, rng As Range) As Boolean
MyMatch = Not IsError(Application.Match(value, rng, 0))
End Function
using separate functions or subs makes it easier (and faster!) to keep control and debug future code changes

Find duplicates in a column and add their corresponding values from another column

I have column A with staff ids and hours worked in column K.
I would like if a staff id appears more than once to add hours worked and put the result in another column corresponding to the first instance of that staff id and the duplicates being 0.
This is for a monthly report and there may be over 2k records at any point.
As everyone else said, a Pivot Table really is the best way. If you're unsure how to use a PivotTable or what it's good for, refer to this SO post where I explain in detail.
Anyway, I put together the below VBA function to help get you started. It's by no means the most efficient approach; it also makes the following assumptions:
Sheet 1 has all the data
A has Staff Id
B has Hours
C is reserved for Total Hours
D will be available for processing status output
This of course can all be changed very easily by altering the code a bit. Review the code, it's commented for you to understand.
The reason a Status column must exist is to avoid processing a Staff Id that was already processed. You could very alter the code to avoid the need for this column, but this is the way I went about things.
CODE
Public Sub HoursForEmployeeById()
Dim currentStaffId As String
Dim totalHours As Double
Dim totalStaffRows As Integer
Dim currentStaffRow As Integer
Dim totalSearchRows As Integer
Dim currentSearchRow As Integer
Dim staffColumn As Integer
Dim hoursColumn As Integer
Dim totalHoursColumn As Integer
Dim statusColumn As Integer
'change these to appropriate columns
staffColumn = 1
hoursColumn = 2
totalHoursColumn = 3
statusColumn = 4
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
totalStaffRows = Sheet1.Cells(Rows.Count, staffColumn).End(xlUp).Row
For currentStaffRow = 2 To totalStaffRows
currentStaffId = Cells(currentStaffRow, staffColumn).Value
'if the current staff Id was not already processed (duplicate record)
If Not StrComp("Duplicate", Cells(currentStaffRow, statusColumn).Value, vbTextCompare) = 0 Then
'get this rows total hours
totalHours = CDbl(Cells(currentStaffRow, hoursColumn).Value)
'search all subsequent rows for duplicates
totalSearchRows = totalStaffRows - currentStaffRow + 1
For currentSearchRow = currentStaffRow + 1 To totalSearchRows
If StrComp(currentStaffId, Cells(currentSearchRow, staffColumn), vbTextCompare) = 0 Then
'duplicate found: log the hours worked, set them to 0, then mark as Duplicate
totalHours = totalHours + CDbl(Cells(currentSearchRow, hoursColumn).Value)
Cells(currentSearchRow, hoursColumn).Value = 0
Cells(currentSearchRow, statusColumn).Value = "Duplicate"
End If
Next
'output total hours worked and mark as Processed
Cells(currentStaffRow, totalHoursColumn).Value = totalHours
Cells(currentStaffRow, statusColumn).Value = "Processed"
totalHours = 0 'reset total hours worked
End If
Next
Application.ScreenUpdating = False
Application.Calculation = xlCalculationAutomatic
End Sub
BEFORE
AFTER
Here is the solution for the data table located in range A1:B10 with headers and results written to column C.
Sub Solution()
Range("c2:c10").Clear
Dim i
For i = 2 To 10
If WorksheetFunction.SumIf(Range("A1:a10"), Cells(i, 1), Range("C1:C10")) = 0 Then
Cells(i, "c") = WorksheetFunction.SumIf( _
Range("A1:a10"), Cells(i, 1), Range("B1:B10"))
Else
Cells(i, "c") = 0
End If
Next i
End Sub
Try below code :
Sub sample()
Dim lastRow As Integer, num As Integer, i As Integer
lastRow = Range("A65000").End(xlUp).Row
For i = 2 To lastRow
num = WorksheetFunction.Match(Cells(i, 1), Range("A1:A" & lastRow), 0)
If i = num Then
Cells(i, 3) = WorksheetFunction.SumIf(Range("A1:A" & lastRow), Cells(i, 1), Range("B1:B" & lastRow))
Else
Cells(i, 1).Interior.Color = vbYellow
End If
Next
End Sub
BEFORE
AFTER
Below code identifies duplicate value in a column and highlight with red. Hope this might be of some help.
iLastRow = Cells(chosenExcelSheet.Rows.Count, 1).End(xlUp).Row 'Determine the last row to look at
Set rangeLocation = Range("A1:A" & iLastRow)
'Checking if duplicate values exists in same column
For Each myCell In rangeLocation
If WorksheetFunction.CountIf(rangeLocation, myCell.Value) > 1 Then
myCell.Interior.ColorIndex = 3'Highlight with red Color
Else
myCell.Interior.ColorIndex = 2'Retain white Color
End If
Next
Sub SelectColoredCells()
Dim rCell As Range
Dim lColor As Long
Dim rColored As Range
'Select the color by name (8 possible)
'vbBlack, vbBlue, vbGreen, vbCyan,
'vbRed, vbMagenta, vbYellow, vbWhite
lColor = RGB(156, 0, 6)
'If you prefer, you can use the RGB function
'to specify a color
'Default was lColor = vbBlue
'lColor = RGB(0, 0, 255)
Set rColored = Nothing
For Each rCell In Selection
If rCell.Interior.Color = lColor Then
If rColored Is Nothing Then
Set rColored = rCell
Else
Set rColored = Union(rColored, rCell)
End If
End If
Next
If rColored Is Nothing Then
MsgBox "No cells match the color"
Else
rColored.Select
MsgBox "Selected cells match the color:" & _
vbCrLf & rColored.Address
End If
Set rCell = Nothing
Set rColored = Nothing
End Sub
this highlights the duplicates

Resources