Excel VBA Macro: Copying relative cell to another worksheet - excel

I am trying to fix missing entry by
finding it and then
copying the most left cell value relative to the found entry to the first empty bottom cell of another worksheet.
With Worksheets("Paste Pivot").Range("A1:AZ1000")
Dim source As Worksheet
Dim destination As Worksheet
Dim emptyRow As Long
Set source = Sheets("Paste Pivot")
Set destination = Sheets("User Status")
Set c = .Find("MissingUserInfo", LookIn:=xlValues)
If Not c Is Nothing Then
firstAddress = c.Address
Do
'Here would go the code to locate most left cell and copy it into the first empty bottom cell of another worksheet
emptyRow = destination.Cells(destination.Columns.Count, 1).End(xlToLeft).Row
If emptyRow > 1 Then
emptyRow = emptyRow + 1
End If
c.End(xlToLeft).Copy destination.Cells(emptyRow, 1)
c.Value = "Copy User to User Status worksheet"
Set c = .FindNext(c)
If c Is Nothing Then Exit Do
Loop While c.Address <> firstAddress
End If
End With

I think CurrentRegion will help you here.
e.g. If you had a value in every cell in the range A1:E4 then
Cells(1,1).CurrentRegion.Rows.Count would equal 4 and
Cells(1,1).CurrentRegion.Columns.Count would equal 5
Therefore you can write:
c.End(xlToLeft).Copy _
destination.Cells(destination.Cells(1).CurrentRegion.Rows.Count + 1, 1)
Provided you don't have any gaps in the middle of your destination spreadsheet, this will copy the user id from the beginning of the line with the "MissingUserInfo" (in the "Paste Pivot" sheet) to the first cell of a new row at the end of the "User Status" sheet.
Your Do Loop then becomes:
Do
c.End(xlToLeft).Copy _
destination.Cells(destination.Cells(1).CurrentRegion.Rows.Count + 1, 1)
c.Value = "Copy User to User Status worksheet"
Set c = .FindNext(c)
If c Is Nothing Then Exit Do
Loop While c.Address <> firstAddress

Answer as originally edited into the question by the original poster:
With Worksheets("Paste Pivot").Range("A1:AZ1000")
Dim source As Worksheet
Dim sourceRowNumber As Long
Dim destination As Worksheet
Dim destCell As Range
Dim destCellRow As Long
Set source = Sheets("Paste Pivot")
Set destination = Sheets("User Status")
Set c = .Find("MissingUserInfo", LookIn:=xlValues)
If Not c Is Nothing Then
firstAddress = c.Address
Do
With destination
Set destCell = .Cells(.Rows.Count, "A").End(xlUp)
destCellRow = destCell.Row + 1
End With
sourceRowNumber = c.Row
destination.Cells(destCellRow, 1).Value = source.Cells(sourceRowNumber, 1)
destination.Cells(destCellRow, 2).Value = source.Cells(sourceRowNumber, 2)
destination.Cells(destCellRow, 3).Value = source.Cells(sourceRowNumber, 3)
c.Value = "Run Macro Again"
Set c = .FindNext(c)
If c Is Nothing Then Exit Do
Loop While c.Address <> firstAddress
End If
End With

Related

How to Automate my Manual Selection Process in VBA

I have a manual selection process that I have tried but failed to automate, so I am reaching out for help. I have attached an image of my Excel sheet as a visual guide when reading my process. Excel Snapshot.
I select cell "L2" and run the code below. It finds the first instance of the value within "A2:J1501" and cuts the whole row. It pastes the row onto the sheet named Lineups. Then it highlights each of the values of the cut row in column "L:L" to let me know that value has been used. I then manually select the next non-highlighted value (in the image example it would be "L2") and run the code again, and again, and again, until every row of L:L is highlighted. This process can take some time depending on the number of rows in L:L so I was hoping I can get some help to automate.
Thank you very much.
Sub ManualSelect()
Dim rng As Range
Set rng = Range("A1:J1501")
Dim ac As Range
Set ac = Application.ActiveCell
rng.Find(what:=ac).Select
Range("A" & ActiveCell.Row).Resize(1, 10).Cut
ActiveWindow.ScrollRow = 1
Sheets("Lineups").Select
nextRow = Cells(Rows.Count, 1).End(xlUp).Row + 1
Cells(nextRow, 1).Select
ActiveSheet.Paste
Sheets("Data").Select
Dim wsData As Worksheet
Dim wsLineups As Worksheet
Dim rngToSearch As Range
Dim rngLineupSet As Range
Dim rngPlayerID As Range
Dim Column As Long
Dim Row As Long
Dim LastRow As Long
Set wsData = Sheets("Data")
Set wsLineups = Sheets("Lineups")
Set rngPlayerID = wsData.Range("L2:K200")
Set rngToSearch = rngPlayerID
LastRow = wsLineups.Cells(Rows.Count, 1).End(xlUp).Row
For Row = 2 To LastRow
For Column = 1 To 10
Set rngLineupSet = rngPlayerID.Find(what:=wsLineups.Cells(Row, Column), LookIn:=xlValues)
If Not rngLineupSet Is Nothing Then rngLineupSet.Interior.Color = 65535
Next Column
Next Row
End Sub
This should be pretty close:
Sub ManualSelect()
Dim wsData As Worksheet, c As Range, dict As Object, v, rw As Range
Dim wsLineups As Worksheet, c2 As Range, f As Range
Set dict = CreateObject("scripting.dictionary") 'for tracking already-seen values
Set wsLineups = ThisWorkbook.Worksheets("Lineups")
Set wsData = ThisWorkbook.Worksheets("Data")
For Each c In wsData.Range("L2", wsData.Cells(Rows.Count, "L").End(xlUp))
v = c.Value
If dict.exists(CStr(v)) Then
c.Interior.Color = vbYellow 'already seen this value in L or a data row
Else
'search for the value in
Set f = wsData.Range("A2:J1501").Find(v, lookat:=xlWhole, LookIn:=xlValues, searchorder:=xlByRows)
If Not f Is Nothing Then
Set rw = f.EntireRow.Columns("A").Resize(1, 10) 'A to J
For Each c2 In rw.Cells 'add all values from this row to the dictionary
dict(CStr(c2)) = True
Next c2
rw.Cut Destination:=wsLineups.Cells(Rows.Count, "A").End(xlUp).Offset(1)
c.Interior.Color = vbYellow
Else
'will there always be a match?
c.Interior.Color = vbRed 'flag no matching row
End If
End If 'haven't already seen this col L value
Next c 'next Col L value
End Sub
I believe this should do it (updated):
Sub AutoSelect()
Dim wsData As Worksheet, wsLineups As Worksheet
Dim rng As Range, listIDs As Range
Set wsData = ActiveWorkbook.Sheets("Data")
Set wsLineups = ActiveWorkbook.Sheets("Lineups")
Set rng = wsData.Range("A2:J1501")
'get last row col L to define list
LastRowL = wsData.Range("L" & Rows.Count).End(xlUp).Row
Set listIDs = wsData.Range("L2:L" & LastRowL)
'loop through all cells in list
For i = 1 To listIDs.Rows.Count
myCell = listIDs.Cells(i)
'retrieve first mach in listID
checkFirst = Application.Match(myCell, listIDs, 0)
'only check first duplicate in list
If checkFirst = i Then
'get new row for target sheet as well (if sheet empty, starting at two)
newrow = wsLineups.Range("A" & Rows.Count).End(xlUp).Row + 1
'check if it is already processed
Set processedAlready = wsLineups.Cells(2, 1).Resize(newrow - 1, rng.Columns.Count).Find(What:=myCell, lookat:=xlWhole, LookIn:=xlValues)
'if so, color yellow, and skip
If Not processedAlready Is Nothing Then
listIDs.Cells(i).Interior.Color = vbYellow
Else
'get fist match for value, if any (n.b. "xlWhole" ensures whole match)
Set foundMatch = rng.Find(What:=myCell, lookat:=xlWhole, LookIn:=xlValues)
'checking for a match
If Not foundMatch Is Nothing Then
'get the row
foundRow = foundMatch.Row - rng.Cells(1).Row + 1
'specify target range and set it equal to vals from correct row in rng
wsLineups.Cells(newrow, 1).Resize(1, rng.Columns.Count).Value2 = rng.Rows(foundRow).Value
'clear contents rng row
rng.Rows(foundRow).ClearContents
'give a color to cells that actually got a match
listIDs.Cells(i).Interior.Color = vbYellow
Else
'no match
listIDs.Cells(i).Interior.Color = vbRed
End If
End If
Else
'duplicate already handled, give same color as first
listIDs.Cells(i).Interior.Color = listIDs.Cells(checkFirst).Interior.Color
End If
Next i
End Sub
Also, I think, slightly faster than the other solution offered (because of the nested loop there?). Update: I got a bit confused about the nested loop in the answer by Tim Williams, but I missed that you also want to "accept" the values in the list that matched on a row that is already gone. I fixed this in the updated version by checking if a value that fails to match on the data range has already been transferred to Lineups. Provided that doing so is permissible, this method avoids the nested loop.
I checked both methods for speed (n = 50) on a list (n = 200) for the full data range, ended up with average of 1.70x faster... But maybe speed is not such a big deal, if you're coming from manual labor :)

Validation summary of mandatory cells in excel

I have got an excel workbook, it has 5 static tabs and more tabs can be created using a template tab.
In each tab there is a certain field or a range that is mandatory to be filled out also in the new created tabs (might be up to 60).
My question is how can I go about seeing in, lets say in mainsheet, a summary which shows me:
Which tab has missing fields
Which fields is missing (an address of a cell)
I tried naming the range "MyRange" and counting if the cells are non blank.
But this will not work for the newly created sheets.
I also tried a conditional formatting but again this will not give me a summary.
In the meantime I also bumped into a sort of solution but this is also not the thing I am looking for:
Sub listEmptyCells()
Dim emptyAddresses() As String
Dim i As Long
Dim ws As Worksheet
Dim rng As Range
Set ws = Sheets("1.Data Source") ' CHANGE AS NECESSARY
Set rng = ws.Range("B30:B32")
If WorksheetFunction.CountBlank(rng) = 0 Then
MsgBox ("No empty cells in the range")
Exit Sub
End If
emptyAddresses() = Split(rng.SpecialCells(xlCellTypeBlanks).Address, ",")
For i = LBound(emptyAddresses) To UBound(emptyAddresses)
ws.Cells(i + 1, 2).Value = emptyAddresses(i)
Next i
End Sub
Your help and guidance here would be highly appreciated
All the best
Jacek
Here's one approach:
Sub listEmptyCells()
Const CHECK_RANGE As String = "B30:B32" 'range to locate empty cells in
Dim i As Long, r As Long, rngCheck As Range, rngEmpty As Range
Dim ws As Worksheet, wb As Workbook, wsSummary As Worksheet
Dim rwSummary As Range, s As String, c As Range
Set wb = ThisWorkbook
Set wsSummary = wb.Worksheets("Summary")
Set rwSummary = wsSummary.Range("A2:B2") 'first row of results
rwSummary.Resize(wb.Worksheets.Count).Clear 'remove previous results
For i = 1 To wb.Worksheets.Count
Set ws = wb.Worksheets(i)
If ws.Name <> wsSummary.Name Then 'exclude specific sheet(s)
s = ""
Set rngEmpty = Nothing
'which range to check - special case or use default?
Select Case ws.Name
Case "Sheet One": Set rngCheck = ws.Range("A1:A10")
Case "Sheet Two": Set rngCheck = ws.Range("G34:G56,H10")
Case Else: Set rngCheck = ws.Range(CHECK_RANGE) 'default range
End Select
'loop cells in check range
For Each c In rngCheck.Cells
If Len(c.Value) = 0 Then
If rngEmpty Is Nothing Then
Set rngEmpty = c
Else
Set rngEmpty = Application.Union(rngEmpty, c)
End If
End If
Next c
If Not rngEmpty Is Nothing Then
s = rngEmpty.Count & " required cell(s) not filled:" & _
rngEmpty.Address(False, False)
End If
With rwSummary 'record results
.Cells(1).Value = ws.Name
.Cells(2).Value = IIf(s <> "", s, "OK")
.Font.Color = IIf(s <> "", vbRed, vbGreen)
End With
Set rwSummary = rwSummary.Offset(1, 0) 'next summary row
End If
Next i
End Sub

VBA copy a found value

I am fairly new to VBA . I have been trying to get this code working to no avail, basically I have a search to find a value (That part is working) and I want to copy that value and the row where this value is located into another sheet on the next empty row and date stamp it. Any help will be appreciated. Many Thanks.
This a sample of the table:
Sample Table
This is the code I have half working:
Sub FindingValues()
Dim val As String
Dim result As String
Dim firstAddress As String
Dim c As Range
val = InputBox("Enter ID")
Set c = Sheets("Sheet1").Range("E:E").Find(val, LookIn:=xlValues, _
MatchCase:=False)
If Not c Is Nothing Then
firstAddress = c.Address
' Application.Goto c
Copy.Sheets(Sheet2).c
Set c = Cells.FindNext(c)
Else
If c Is Nothing Then
MsgBox "Could Not Find " & Res
End If
End If
I think this should do it...
Sub FindingValues()
Dim val As String, result As String, firstAddress As String, entryROW As Long
Dim c As Range
'PGCodeRider making assumption to inser in column A
Dim columnNumberToPasteData As Long
columnNumberToPasteData = 1
'assumes Sheet2 is where data should be copied
Dim WS2 As Worksheet
Set WS2 = Sheets("Sheet2")
val = InputBox("Enter ID")
'probably want something like this so that if user wants to cancel
If val = "" Then Exit Sub
Set c = Sheets("Sheet1").Range("E:E").Find(val, LookIn:=xlValues, _
MatchCase:=False)
If Not c Is Nothing Then
entryROW = WS2.Cells(Rows.Count, columnNumberToPasteData).End(xlUp).Row + 1
WS2.Rows(entryROW).Value = c.Worksheet.Rows(c.Row).Value
WS2.Cells(entryROW, Columns.Count).End(xlToLeft).Offset(0, 1).Value = VBA.Now
' With WS2.Cells(entryROW, columnNumberToPasteData)
' .Offset(0, 0).Value = c.Value
' .Offset(0, 1).Value = c.Row
' .Offset(0, 2).Value = Now()
' End With
'
Else
If c Is Nothing Then MsgBox "Could Not Find " & val
End If
End Sub

Excel - VBA - Search for a specific value within a cell

Is it possible to search for a specific value in a column?
I want to be able to search all of the cells in column "B" and look for the 'word' "pip" in it (without being case sensitive). I've got everything else, just need to know if this is possible or how it can be done.
My Current code looks as follows:
Sub A()
ActiveSheet.Name = "Data"
Dim ws As Worksheet
Set ws = Sheets("Data")
Dim ws1 As Worksheet
Set ws1 = ThisWorkbook.Sheets.Add(After:= _
ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
ws1.Name = "pip"
ws.Activate
Row = 2
Dim i As Integer
For i = 1 To 10
If (Cells(i, 2).Value = (HAS pip IN IT) Then 'This is the part that i'm struggling with
Copied = ws.Range(Cells(i, 1), Cells(i, 17)).Value 'If possible, this would cut and paste so it deleted the original
ws1.Activate
ws1.Range(Cells(Row, 1), Cells(Row, 17)).Value = Copied
Row = Row + 1
ws.Activate
End If
Next i
End Sub
Edit: Just to clarify, the value in column B will never just be "pip". It will be a full sentence but if it contains "pip" then i would like the IF function to work.
Find and FindNext work nicely (and quickly!)
'...
Dim copyRange As Range
Dim firstAddress As String
Set copyRange = ws.Range("B1:B1500").Find("pip", , , xlPart)
If Not copyRange Is Nothing Then
firstAddress = copyRange.Address
Do
ws2.Range(Cells(Row, 1), Cells(Row, 17)).Value = Intersect(copyRange.EntireRow, ws.Columns("A:Q")).Value
Row = Row + 1
Set copyRange = Range("B1:B10").FindNext(copyRange)
Loop While copyRange.Address <> firstAddress
End If
'...

Read all column cells if data available and skip empty cells using macro

I have a macro that reads two xls sheets that compares the data and copy the match into the next column. But the only problem with my code is if is there any empty cell comes in between it stops thinking that it is the end of the file. I have a scenario where I have some blank cells in the column which I need to read and may be data is available in next row.
macro file:
Sub findAndReplace()
'Declare working sheet objects
Dim rSh1 As Range, rSh2 As Range, rFound As Range, r As Range
Dim strShortName As String
strShortName = Cells(2, 3)
'Check if the source file is opened or not, if not open it
checkFileOpened (strShortName)
'Read the source file for mapping
With Workbooks(strShortName).Worksheets("sheet1")
Set rSh1 = .Range("B2", .Range("B2").End(xlDown))
End With
'Read the current working sheet for given range to match with source data
With Worksheets("sheet1")
Set rSh2 = .Columns("A:a")
End With
'Loop through for a match and replace it
For Each r In rSh1
With r
Set rFound = rSh2.Find(what:=.Value, lookat:=xlWhole)
If Not rFound Is Nothing Then
.Offset(0, 1) = rFound.Offset(0, 1).Value
Else
.Offset(0, 1) = "Not Found"
End If
End With
Next r
End Sub
Try replacing Set rSh1 = .Range("B2", .Range("B2").End(xlDown)) with Set rSh1 = Range(.Cells(2,2), .Cells(rows.Count,2).End(xlUp)). I don't see any logic to skip a blank cell (r). You may want to add that, too.
Sub findAndReplace()
'Declare working sheet objects
Dim rSh1 As Range, rSh2 As Range, rFound As Range, r As Range
Dim strShortName As String
strShortName = Cells(2, 3)
'Check if the source file is opened or not, if not open it
checkFileOpened (strShortName)
'Read the source file for mapping
With Workbooks(strShortName).Worksheets("sheet1")
Set rSh1 = Range(.Cells(2,2), .Cells(rows.Count,2).End(xlUp))
End With
'Read the current working sheet for given range to match with source data
With Worksheets("sheet1")
Set rSh2 = .Columns("A:a")
End With
'Loop through for a match and replace it
For Each r In rSh1
If Not r is Nothing
Set rFound = rSh2.Find(what:=.Value, lookat:=xlWhole)
If Not rFound Is Nothing Then
r.Offset(0, 1) = rFound.Offset(0, 1).Value
Else
r.Offset(0, 1) = "Not Found"
End If
End If
Next r
End Sub
So I did a little modification to the answer given by #Brian and here is my working solution
Sub findAndReplace()
'Declare working sheet objects
Dim rSh1 As Range, rSh2 As Range, rFound As Range, r As Range
Dim strShortName As String
strShortName = Cells(2, 3)
'Check if the source file is opened or not, if not open it
checkFileOpened (strShortName)
'Read the source file for mapping
With Workbooks(strShortName).Worksheets("sheet1")
Set LastCellB = .Cells(.Rows.Count, "B").End(xlUp)
Set rSh1 = .range("B2", LastCellB)
End With
'Read the current working sheet for given range to match with source data
With Worksheets("sheet1")
Set rSh2 = .Columns("A:a")
End With
'Loop through for a match and replace it
For Each r In rSh1
With r
Set rFound = rSh2.Find(What:=.Value, LookAt:=xlWhole)
If Not rFound Is Nothing Then
.Offset(0, 1) = rFound.Offset(0, 1).Value
Else
.Offset(0, 1) = "Not Found"
End If
End With
Next r
End Sub

Resources