Copy Paste a cell based on Row & Column - excel

What I'd like my sheet to do is when the user has updated the values in the cells D3:D8 on the sheet "Buffy Cast" they can press the button and these values will be copied into the tab "Actual FTE". The tab "Actual FTE" has a table with multiple dates and the ID of the person. The code should find the column based on the date in the "Buffy Cast" sheet, and then the row ID, copying the data across to this location.
I admit to resurrecting some dictionary code to find the rows, which actually worked, but I'm having issues getting it to find the column. Sheets and code below, huge thank yous.
Validation Sheet
Blank Actuals Sheet
What I'd like to happen on the actuals sheet
and finally my code
Option Explicit
Sub Update()
Dim wsValidate As Worksheet, wsActual As Worksheet
Dim lrValidate As Long, lrActual As Long
Dim i As Long, r As Long, rc As Variant
Dim n As Long, m As Long
Dim dict As Object, key As String
Set dict = CreateObject("Scripting.Dictionary")
Set wsValidate = Worksheets("BuffyCast")
Set wsActual = Worksheets("ActualFTE")
Dim sourceWS As Worksheet, targetWS As Worksheet
Dim lastCol As Long, lastRow As Long, srcRow As Range
Dim found1 As Range, j As Long, Cr1 As String
'Find column
With wsActual
lastCol = .Cells(2, Columns.Count).End(xlToLeft).Column
For j = 1 To lastCol
Cr1 = Worksheets("BuffyCast").Range("D2")
Set srcRow = .Range("A2", .Cells(2, lastCol))
Set found1 = srcRow.Find(What:=Cr1, LookAt:=xlWhole, MatchCase:=False)
Next
End With
'Make dictionary
With wsActual
lrActual = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = 2 To lrActual
key = Trim(.Cells(i, "A"))
If dict.exists(key) Then
MsgBox "Duplicate ID No '" & key & "'", vbCritical, "Row " & i
Exit Sub
ElseIf Len(key) > 0 Then
dict.Add key, i
End If
Next
End With
With wsValidate
lrValidate = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = 2 To lrValidate
key = Trim(.Cells(i, "A"))
If dict.exists(key) Then
r = dict(key)
wsActual.Cells(r, found1) = .Cells(i, "D")
n = n + 1
Else
.Rows(i).Interior.Color = RGB(255, 255, 0)
m = m + 1
End If
Next
End With
MsgBox n & "Actual FTE Update" & vbLf & m & " rows not found", vbInformation
End Sub

You can use the WorksheetFunction.Match method to find a value in a row:
Dim Col As Long
On Error Resume Next
Col = Application.WorksheetFunction.Match(wsValidate.Range("D2").Value2, wsActual.Rows(2), 0)
On Error GoTo 0
If Col = 0 Then
MsgBox "Column was not found", vbCritical
Exit Sub
End If
' here col has the column number you are looking for
' and you can write to that column like
wsActual.Cells(RowNumber, Col).Value = 123
This will find the value of wsValidate.Range("D2") in the second row of wsActual.

Related

Excel VBA, Check values from columns between sheets and delete duplicate

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

How to copy entire rows based on column A duplicated name to its respective worksheet in VBA?

My current code will attempt to copy entire rows based on the column A duplicated name to its respective worksheet using VBA as shown below. But it only works for the 1st duplicated name but not the rest. When i review my code, i realised that my target(at the part for target=Lbound to Ubound part) is always 0 so i was wondering why is it always 0 in this case? Because it suppose to be ranging from 0 to 3?
Sub test()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim cs As Worksheet
Dim mycell As Range, RANG As Range, Mname As String, Rng As Range
Dim r As Range, dict As Object
Set dict = CreateObject("Scripting.Dictionary")
With Sheets(1)
' Build a range (RANG) between cell F2 and the last cell in column F
Set RANG = Range(.Cells(2, "A"), .Cells(.Rows.count, "A").End(xlUp))
End With
' For each cell (mycell) in this range (RANG)
For Each mycell In RANG
Mname = mycell.Value
' If the count of mycell in RANG is greater than 1, then set the value of the cell 1 across to the right of mycell (i.e. column G) as "Duplicate Found"
If Application.WorksheetFunction.CountIf(RANG, mycell.Value) > 1 Then
If dict.count > 0 And dict.Exists(Mname) Then
dict(Mname) = mycell.Row()
Else
dict.Add Mname, mycell.Row()
End If
End If
Next mycell
Dim x As Long, Target As Long, i As Long
Dim CopyMe As Range
'Dim Arr: Arr = Array(Key)
Dim f As Variant
For x = 1 To 4
Set cs = ThisWorkbook.Sheets.Add(After:=Sheets(ThisWorkbook.Sheets.count))
cs.Name = "Names" & x
Next x
'Display result in debug window (Modify to your requirement)
Startrow = 2
For Each Key In dict.Keys
Set Rng = ws.Range("A" & Startrow & ":A" & dict(Key))
'Create 3 Sheets, move them to the end, rename
lr = dict(Key)
v = dict.Keys 'put the keys into an array
'Loop through each name in array
For Target = LBound(v) To UBound(v) - 1 '<-------why is Target always 0 here?
'Loop through each row
For i = Startrow To lr
'Create Union of target rows
If ws.Range("A" & i) = v(Target) Then
If Not CopyMe Is Nothing Then
Set CopyMe = Union(CopyMe, ws.Range("A" & i))
Else
Set CopyMe = ws.Range("A" & i)
End If
End If
Next i
Startrow = dict(Key) + 1
'Copy the Union to Target Sheet
If Not CopyMe Is Nothing And Target = 0 Then
CopyMe.EntireRow.Copy Destination:=ThisWorkbook.Sheets("Names1").Range("A1")
Set CopyMe = Nothing
End If
If Not CopyMe Is Nothing And Target = 1 Then
CopyMe.EntireRow.Copy Destination:=ThisWorkbook.Sheets("Names2").Range("A1")
Set CopyMe = Nothing
End If
If Not CopyMe Is Nothing And Target = 2 Then
CopyMe.EntireRow.Copy Destination:=ThisWorkbook.Sheets("Names3").Range("A1")
Set CopyMe = Nothing
End If
If Not CopyMe Is Nothing And Target = 3 Then
CopyMe.EntireRow.Copy Destination:=ThisWorkbook.Sheets("Names4").Range("A1")
Set CopyMe = Nothing
End If
Next Target
Next
End Sub
Main worksheet
In the case of duplicated John name:
In the case of duplicated Alice name
Updated code:
Sub test()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim cs As Worksheet
Dim mycell As Range, RANG As Range, Mname As String, Rng As Range
Dim r As Range, dict As Object
Set dict = CreateObject("Scripting.Dictionary")
With Sheets(1)
' Build a range (RANG) between cell F2 and the last cell in column F
Set RANG = Range(.Cells(2, "A"), .Cells(.Rows.Count, "A").End(xlUp))
End With
' For each cell (mycell) in this range (RANG)
For Each mycell In RANG
Mname = mycell.Value
' If the count of mycell in RANG is greater than 1, then set the value of the cell 1 across to the right of mycell (i.e. column G) as "Duplicate Found"
If Application.WorksheetFunction.CountIf(RANG, mycell.Value) > 1 Then
If dict.Count > 0 And dict.Exists(Mname) Then
dict(Mname) = mycell.Row()
Else
dict.Add Mname, mycell.Row()
End If
End If
Next mycell
Dim StartRow As Long
StartRow = 2
Dim Key As Variant
Dim lr As Long, v As Variant
For Each Key In dict.Keys
Set Rng = ws.Range("A" & StartRow & ":A" & dict(Key))
lr = dict(Key)
v = dict.Keys 'put the keys into an array
'Create 3 Sheets, move them to the end, rename
'Loop through each name in array
For Target = LBound(v) To UBound(v) - 1 '<-------why is Target always 0 here?
'Loop through each row
For i = StartRow To lr
'Create Union of target rows
If ws.Range("A" & i) = v(Target) Then
If Not CopyMe Is Nothing Then '<---object required error at If Not copyme...
Set CopyMe = Union(CopyMe, ws.Range("A" & i))
Else
Set CopyMe = ws.Range("A" & i)
End If
End If
Next i
StartRow = dict(Key) + 1
'Copy the Union to Target Sheet
If Not CopyMe Is Nothing Then
Mname = "Name" & CStr(Target + 1)
CopyMe.EntireRow.Copy Destination:=ThisWorkbook.Sheets(Mname).Range("A1")
Set CopyMe = Nothing
End If
Next Target
Next Key
End Sub
Use a dictionary for the start row and another for the end row. It is then straightforward to determine the range of duplicate rows for each name and copy them to a new sheet.
Sub CopyDuplicates()
Dim wb As Workbook, ws As Worksheet
Dim irow As Long, iLastRow As Long
Dim dictFirstRow As Object, dictLastRow As Object, sKey As String
Set dictFirstRow = CreateObject("Scripting.Dictionary") ' first row for name
Set dictLastRow = CreateObject("Scripting.Dictionary") ' last row for name
Set wb = ThisWorkbook
Set ws = wb.Sheets("Sheet1")
iLastRow = ws.Range("A" & Rows.Count).End(xlUp).Row
' build dictionaries
For irow = 1 To iLastRow
sKey = ws.Cells(irow, 1)
If dictFirstRow.exists(sKey) Then
dictLastRow(sKey) = irow
Else
dictFirstRow.Add sKey, irow
dictLastRow.Add sKey, irow
End If
Next
' copy range of duplicates
Dim k, iFirstRow As Long, rng As Range, wsNew As Worksheet
For Each k In dictFirstRow.keys
iFirstRow = dictFirstRow(k)
iLastRow = dictLastRow(k)
' only copy duplicates
If iLastRow > iFirstRow Then
Set wsNew = wb.Worksheets.Add(after:=wb.Sheets(wb.Sheets.Count))
wsNew.Name = k
Set rng = ws.Rows(iFirstRow & ":" & iLastRow).EntireRow
rng.Copy wsNew.Range("A1")
Debug.Print k, iFirstRow, iLastRow, rng.Address
End If
Next
MsgBox "Done"
End Sub
I couldn't find a mistake because I didn't want to set up the workbook that would enable me to test your code thoroughly. However, I did read through your code and found that you were very lax on declaring variables. I suggest you enter Option Explicit at the top of your code.
To call a Key a "Key" is asking for trouble. Best practice suggests that you don't use VBA key words as variable names. In the context of your code, For Each Key In Dict.Keys requires Key to be a variant. Being undeclared would make it a variant by default but if it's also a word VBA reserves for its own use confusion might arise.
Another idea is that you might have put a break point on For Target = LBound(v) To UBound(v) - 1. When the code stops there Target will be zero because the line hasn't executed yet. But after the first loop execution will not return to this line. So you might have missed Target taking on a value and the error might be elsewhere. Make sure you place the break point on the first line after the For statement. You might also add Debug.Print LBound(v), UBound(v) before the For statement or check these values in the Locals window.
Below is the section of the code where I added several variable declarations and made an amendment to the code that creates and names the new sheets.
Dim StartRow As Long
StartRow = 2
Dim Key As Variant
Dim lr As Long, v As Variant
For Each Key In Dict.Keys
Set Rng = Ws.Range("A" & StartRow & ":A" & Dict(Key))
lr = Dict(Key)
v = Dict.Keys 'put the keys into an array
'Create 3 Sheets, move them to the end, rename
'Loop through each name in array
For Target = LBound(v) To UBound(v) - 1 '<-------why is Target always 0 here?
'Loop through each row
For i = StartRow To lr
'Create Union of target rows
If Ws.Range("A" & i) = v(Target) Then
If Not CopyMe Is Nothing Then
Set CopyMe = Union(CopyMe, Ws.Range("A" & i))
Else
Set CopyMe = Ws.Range("A" & i)
End If
End If
Next i
StartRow = Dict(Key) + 1
'Copy the Union to Target Sheet
If Not CopyMe Is Nothing Then
Mname = "Name" & CStr(Target + 1)
CopyMe.EntireRow.Copy Destination:=ThisWorkbook.Sheets(Mname).Range("A1")
Set CopyMe = Nothing
End If
Next Target
Next Key
John, I spent an hour working my way through your code - correcting and commenting. I got a real good feeling of how confidence escaped from your mind as you went into the last third of the code. The same thing happened to me. I saw, as you probably did, that the concept was so far off the mark that it is very hard to salvage. So I wrote code that probably does what you want. Please try it.
Sub TransferData()
Dim Src As Variant ' source data
Dim Ws As Worksheet ' variable target sheet
Dim WsName As String
Dim Rl As Long ' last row
Dim R As Long ' row
Dim C As Long ' column
With ThisWorkbook.Sheets("TestData")
' Copy all values between cell A2 and the last cell in column F
Rl = .Cells(.Rows.Count, "A").End(xlUp).Row
Src = Range(.Cells(2, "A"), .Cells(Rl, "F")).Value
End With
Application.ScreenUpdating = False
For R = 1 To UBound(Src)
WsName = Trim(Split(Src(R, 1))(0)) ' first word in A2 etc
On Error Resume Next
Set Ws = Worksheets(WsName)
If Err Then
With ThisWorkbook.Sheets
Set Ws = .Add(After:=Sheets(.Count))
End With
Ws.Name = WsName
End If
On Error Goto 0
' append data
With Ws
Rl = .Cells(.Rows.Count, "A").End(xlUp).Row
For C = 1 To UBound(Src, 2)
With .Rows(Rl + 1)
.Cells(C).Value = Src(R, C)
End With
Next C
End With
Next R
Application.ScreenUpdating = True
End Sub
The code doesn't use a dictionary. That's why it is much shorter and much more efficient, too. It just sorts the data directly to different sheets based on what it finds in column A. There is no limit to the number of sheets you might need.
Observe that the sheet on which I had the data is called "TestData" in this code. It should be the one in your project that responded to the moniker Sheets(1), most likely aka ThisWorkbook.Worksheets("Sheet1").

store rows in vba dictionary A1:D8 and check if cell value in column A matches unique key in column K if match then paste

Dim Cell As Range
Dim Data As Variant
Dim Dict As Object
Dim Item As Variant
Dim Key As Variant
Dim Rng As Range
Dim RngBeg As Range
Dim RngEnd As Range
Dim Wks As Worksheet
Set Wks = ThisWorkbook.activesheet
Set RngBeg = Wks.Range("A1:D8")
Set RngEnd = Wks.Cells(Rows.Count, RngBeg.Column).End(xlUp)
If RngEnd.Row < RngBeg.Row Then Exit Sub
Set Rng = Wks.Range(RngBeg, RngEnd)
Set Dict = CreateObject("Scripting.Dictionary")
Dict.CompareMode = vbTextCompare
For Each Cell In Rng.Columns(1).Cells
Key = Trim(Cell)
Item = Cell.Resize(1, Rng.Columns.Count).Value
With activesheet
For Each Cell In .Range("k2", .Range("k" & Rows.Count).End(xlUp))
If Dict.exists(Cl.Value) Then Cell.Offset(, 1).Value = Dic(Cell.Value)
Next Cell
End With
In column K I have values : 98,34,78,11
and in column A I have :98,98,98,11,34,78,78
The dictionary stores each row in col A:D
for example:
98,east,phone,address
98,west,mobile,na
and then checks if the first cell in A1: 98 matches with column K and if it does paste row A1:D1 next to row in column K corresponding to 98 and insert a row if there several matches i.e 3 98s in column A.
The problem occurs here where it is supposed to paste the values next to the unique keys ie 98 in column K but does not do so:
With activesheet
For Each Cell In .Range("k2", .Range("k" & Rows.Count).End(xlUp))
If Dict.exists(Cell.Value) Then Cell.Offset(, 1).Value = Dic(Cell.Value)
Next Cell
End With
Could someone please suggest what is going wrong?
I have edited code referenced from here:
http://www.vbaexpress.com/forum/showthread.php?60005-Store-rows-in-dictionary-or-collection
https://www.mrexcel.com/board/threads/vba-to-find-match-of-cell-value-and-copy-adjacent-cell-when-match-found.1112978/
Dictionary Keys must be unique so if you build it from column A then any previous values will be replaced as you scan down.
Assuming column K values are unique then use that column to build the dictionary using cell as key and row number as value. Then by scanning column A the dictionary will give the target row for Col A:D to be copied to. It gets more complicated when adding rows because the dictionary has to be refreshed.
Sub match()
'https://stackoverflow.com/questions/60681813/store-rows-in-vba-dictionary-a1d8-and-check-if-cell-value-in-column-a-matches-u
Const COL_DICT = "K"
Const COL_DATA = "A"
Const COLS_COPY = 4
Dim wb As Workbook, ws As Worksheet, i As Long
Dim iRow As Long, iLastRow As Long, iLastDictRow As Long
Dim iTargetRow As Long, rngTarget As Range
Dim Dict As Object, sKey As String
Set wb = ThisWorkbook
Set ws = wb.ActiveSheet ' sheets("Sheet Name") would be better
Set Dict = CreateObject("Scripting.Dictionary")
Dict.CompareMode = vbTextCompare
'build dictionary as lookup to row no
iLastDictRow = ws.Range(COL_DICT & Rows.Count).End(xlUp).Row
For iRow = 2 To iLastDictRow
sKey = CStr(ws.Cells(iRow, COL_DICT))
If Dict.exists(sKey) Then
MsgBox "Duplicate key " & sKey & " at row " & iRow, vbCritical
Exit Sub
Else
Dict.Add sKey, iRow
End If
Next
' scan down data
iLastRow = ws.Range(COL_DATA & Rows.Count).End(xlUp).Row
For iRow = 2 To iLastRow
sKey = ws.Cells(iRow, COL_DATA)
If Dict.exists(sKey) Then
iTargetRow = Dict(sKey)
Set rngTarget = ws.Cells(iTargetRow, COL_DICT)
If rngTarget.Offset(0, 1).Value = "" Then
ws.Cells(iRow, 1).Resize(1, COLS_COPY).Copy rngTarget
Else
' add row
rngTarget.Offset(1, 0).Resize(1, COLS_COPY).Insert _
Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
ws.Cells(iRow, 1).Resize(1, COLS_COPY).Copy rngTarget.Offset(1, 0)
' rebuild dictionary after added row
iLastDictRow = iLastDictRow + 1
For i = 2 To iLastDictRow
sKey = CStr(ws.Cells(i, COL_DICT))
Dict(sKey) = i
Next
End If
End If
Next
MsgBox iLastRow - 1 & " rows scanned in col " & COL_DATA, vbInformation, "Finished"
End Sub

Copy the data from one sheet and paste it on the other sheet

I need an excel vba code which copy the data from one sheet and paste it on the other sheet if the given conditions satisfied. There will be two sheets in a workbook (sheet1 and sheet 2). Basically the data in sheet 2 column "C" must be copy to sheet 1 column "C".
The conditions are : -
There will be three columns in SHEET 1&2 A,B,C .
IF SHEET 1 B1 has a data let us take("88").Now,it should search how many of them ("88") are there in sheet2 B:B.
If there are more than one let us take "4" then those "4" sheet2 "C" values are belongs to the sheet 1
"A1". It should create another three rows with "sheet1 A1 & B1" Value then those 4 values must be
paste in "sheet1 "c" beside those four "Sheet A1&B1". iam unable to select those 4 SHEET2 "C" VALUES
If there is one "88" then it can just paste at sheet1"C1".
In this way it should do for every value in sheet 1 B:B.
At least Tell me what code is used to add rows with cell value through vba
How To Find Value & Copy Corresponding Cell
Sub copythedata()
Dim r As Long, ws As Worksheet, wd As Worksheet
Dim se As String
Dim sf As String
Dim fn As Integer
Dim y As Integer
Dim lrow As Long
Set ws = Worksheets("sheet2")
Set wd = Worksheets("sheet1")
y = 123
x = wd.Cells(Rows.Count, 1).End(xlUp).Row
MsgBox "Last Row: " & x
If x > y Then
wd.Range(wd.Cells(y, 1), wd.Cells(x, 1)).EntireRow.Delete Shift:=xlUp
End If
For r = wd.Cells(Rows.Count, "B").End(xlUp).Row To 2 Step -1
fn = Application.WorksheetFunction.countif(ws.Range("B:B"), wd.Range("B" & r).Value)
If fn = 1 Then
wd.Range("C" & r).Value = ws.Range("C" & r).Value
ElseIf fn > 1 Then
se = wd.Range(wd.Cells(A, r), wd.Cells(B, r)).EntireRow.Copy
wd.Range("A123").Rows(fn - 1).Insert Shift:=xlShiftDown
Else
wd.Range("C" & r).Value = "NA"
End If
Next r
End Sub
See Find and
FindNext
When using FindNext see the Remarks section for how to stop search after the 'wraparound' to the start, otherwise you get into an endless loop.
Option Explicit
Sub copythedata()
Dim wb As Workbook, ws1 As Worksheet, ws2 As Worksheet
Dim iLastRow1 As Integer, iLastRow2 As Long
Dim iRow As Integer, iNewRow As Long, iFirstFound As Long
Dim rngFound As Range, rngSearch As Range
Dim cell As Range, count As Integer
Set wb = ThisWorkbook
Set ws1 = wb.Sheets("Sheet1")
Set ws2 = wb.Sheets("sheet2")
' sheet 2 range to search
iLastRow2 = ws2.Range("B" & Rows.count).End(xlUp).Row
Set rngSearch = ws2.Range("B1:B" & iLastRow2)
'Application.ScreenUpdating = False
' sheet1 range to scan
iLastRow1 = ws1.Range("B" & Rows.count).End(xlUp).Row
' add new rows after a blank row to easily identify them
iNewRow = iLastRow1 + 1
For iRow = 1 To iLastRow1
Set cell = ws1.Cells(iRow, 2)
Set rngFound = rngSearch.Find(what:=cell.Value, _
LookIn:=xlValues, LookAt:=xlWhole, SearchDirection:=xlNext)
If rngFound Is Nothing Then
'Debug.Print "Not found ", cell
cell.Offset(0, 1) = "NA"
Else
iFirstFound = rngFound.Row
Do
'Debug.Print cell, rngFound.Row
If rngFound.Row = iFirstFound Then
cell.Offset(0, 1) = rngFound.Offset(0, 1).Value
Else
iNewRow = iNewRow + 1
ws1.Cells(iNewRow, 1) = cell.Offset(, -1)
ws1.Cells(iNewRow, 2) = cell.Offset(, 0)
ws1.Cells(iNewRow, 3) = rngFound.Offset(0, 1).Value
End If
Set rngFound = rngSearch.FindNext(rngFound)
Loop Until rngFound.Row = iFirstFound
End If
Next
Application.ScreenUpdating = True
MsgBox "Finished", vbInformation
End Sub

Remove Duplicates in a Column and enter Sum in another Column

I want to remove duplicates based on the text in Column I and sum the values in Column C, the data in the other columns doesn't matter.
I do not want a pivot table and I am aware they are the preferred option for this type of thing.
An example of what I'd like to achieve:
I found VBA code and tried to modify it. It doesn't delete all the lines.
Sub Sum_and_Dedupe()
With Worksheets("data")
'deal with the block of data radiating out from A1
With .Cells(1, 1).CurrentRegion
'step off the header and make one column wider
With .Resize(.Rows.Count - 1, .Columns.Count + 1).Offset(1, 0)
.Columns(.Columns.Count).Formula = "=sumifs(c:c, i:i, i2)"
.Columns(3) = .Columns(.Columns.Count).Value
.Columns(.Columns.Count).Delete
End With
'remove duplicates
.RemoveDuplicates Columns:=Array(9), Header:=xlYes
End With
.UsedRange
End With
End Sub
This should be an answer to your question.
However, code might require adaptation if the range in which you look becomes very long.
Option Explicit
Sub test()
Dim wb As Workbook
Dim ws As Worksheet
Dim LastRow As Long, LastCol As Long, a As Double, i As Long
Dim Rng As Range
Dim Cell As Variant, Estimate As Variant
Set wb = ThisWorkbook
Set ws = wb.Sheets(1)
LastRow = ws.Cells(ws.Rows.Count, "I").End(xlUp).Row
LastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
Set Rng = ws.Range(ws.Cells(2, 9), ws.Cells(LastRow, 9))
For Each Cell In Rng
i = 0
a = 0
For Each Estimate In Rng
If Estimate.Value = Cell.Value Then
i = i + 1 'Count nr of intances
a = a + ws.Cells(Estimate.Row, 3).Value 'sum booking value
If i > 1 Then
ws.Rows(Estimate.Row).Delete
i = 1
LastRow = LastRow - 1
End If
End If
Next Estimate
ws.Cells(Cell.Row, 3).Value = a 'Enter sum in booked this week
Next Cell
End Sub
You'll either need to change your current sheet name to data, or change the first two lines of this code to fit your needs. sh = the data sheet that you showed us. osh = an output sheet that this code will generate. Note also if column C or I move you can update the positions easily by changing colBooked and colEstimate. If you have more than a thousand unique estimate entries then make the array number larger than 999.
Sub summariseEstimates()
Dim sh As String: sh = "data"
Dim osh As String: osh = "summary"
Dim colBooked As Integer: colBooked = 3
Dim colEstimate As Integer: colEstimate = 9
Dim myArray(999) As String
Dim shCheck As Worksheet
Dim output As Worksheet
Dim lastRow As Long
Dim a As Integer: a = 0
Dim b As Integer
Dim r As Long 'row anchor
Dim i As Integer 'sheets
'Build summary array:
With Worksheets(sh)
lastRow = .Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
For r = 2 To lastRow
If r = 2 Then 'first entry
myArray(a) = .Cells(r, colEstimate) & "," & .Cells(r, colBooked)
Else
For b = 0 To a
If VBA.LCase(VBA.Replace(.Cells(r, colEstimate), " ", "")) = VBA.LCase(VBA.Replace(VBA.Split(myArray(b), ",")(0), " ", "")) Then 'match
myArray(b) = VBA.Split(myArray(b), ",")(0) & "," & VBA.Split(myArray(b), ",")(1) + .Cells(r, colBooked)
Exit For
End If
Next b
If b = a + 1 Then 'completed loop = no match, create new array item:
a = a + 1
myArray(a) = .Cells(r, colEstimate) & "," & .Cells(r, colBooked)
End If
End If
Next r
End With
'Create summary sheet:
On Error Resume Next
Set shCheck = Worksheets(osh)
If Err.Number <> 0 Then
On Error GoTo 0
Set output = Worksheets.Add(After:=Worksheets(sh))
output.Name = osh
Err.Clear
Else
On Error GoTo 0
If MsgBox("*" & osh & "* sheet already exists. Proceed to delete and recreate?", vbOKCancel, "Summary") = vbCancel Then
Exit Sub
Else
Application.DisplayAlerts = False
Worksheets(osh).Delete
Set output = Worksheets.Add(After:=Worksheets(sh))
output.Name = osh
End If
End If
'Output to summary sheet:
With Worksheets(osh)
.Cells(1, 1).Value = "ESTIMATE"
.Cells(1, 2).Value = "BOOKED THIS WEEK"
For b = 0 To a
.Cells(b + 2, 1).Value = VBA.Split(myArray(b), ",")(0)
.Cells(b + 2, 2).Value = VBA.Split(myArray(b), ",")(1)
Next b
.Columns("A:B").AutoFit
End With
End Sub

Resources