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

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

Related

Selecting element from VBA union

I have a task to make a VBA macro based on few sections in .xls file.
I know that in this file it will always be three sections which starts with specific name in example file "Block". But starting row where "Block" is written each time could be different.
Example of .xls file:
enter image description here
My approach had been to search for address of each column containing string "Block"
And later make further code based on knowing there start of each block are.
My code so far:
Public Values
Sub Macro1()
FindAll ("Block")
Debug.Print Values
'
End Sub
Sub FindAll(text)
Dim fnd As String, FirstFound As String
Dim FoundCell As Range, rng As Range
Dim myRange As Range, LastCell As Range
fnd = text
Set myRange = ActiveSheet.UsedRange
Set LastCell = myRange.Cells(myRange.Cells.Count)
Set FoundCell = myRange.Find(what:=fnd, after:=LastCell)
'Test to see if anything was found
If Not FoundCell Is Nothing Then
FirstFound = FoundCell.Address
Else
GoTo NothingFound
End If
Set rng = FoundCell
'Loop until cycled through all unique finds
Do Until FoundCell Is Nothing
'Find next cell with fnd value
Set FoundCell = myRange.FindNext(after:=FoundCell)
'Add found cell to rng range variable
Set rng = Union(rng, FoundCell)
'Test to see if cycled through to first found cell
If FoundCell.Address = FirstFound Then Exit Do
Loop
'Creates global value with all found adresses
Values = rng.Address
Exit Sub
Output Is received as intended:
$A$5,$A$8,$A$1
However I struggle to select element for further coding.
I tried:
Debug.Print Values.Rows.item(1).Adress
Debug.Print Values.Rows.item(1,1).Adress
Debug.Print Values.Rows.item(1)
Debug.Print Values.Rows.item(1,1)
But it yields "Run-time error '424' "
My desired output would be to create three variables containing addresses for these sections.
That
Debug.Print Section_1
Debug.Print Section_2
Debug.Print Section_3
Would yield:
$A$1
$A$5
$A$8
Is there a way to select nth element from union in VBA?
If you want to access the single cells of the range, you can simply loop over it:
Dim cell as Range
For Each cell In rng
Debug.Print cell.Address
Next
Could also be done using an index:
Dim i As Long
For i = 1 To rng.Count
Debug.Print rng(i).Address
Next
Now in your example, you combine single cells using Union. If you combine larger ranges and want to access those ranges, you can use the Areas-Property. However, Excel will optimize the areas, if you do Union(Range("A1"), Range("A2)), you will end up with one area A1:A2.
With ActiveSheet
Set rng = Union(.Range("D5:E16"), .Range("A1:F12"), .Range("X4"))
End With
Dim a As Range
For Each a In rng.Areas
Debug.Print a.Address
Next
For i = 1 to rng.Areas.Count
Debug.Print rng(i).Address
Next
Btw: Every Range (even a single cell) has the Areas-property set, so it's always safe to loop over the Areas of a range.
Try,
Public Values
Public rngDB() As Range
Sub Macro1()
Dim i As Integer
FindAll ("Block")
Debug.Print Values
For i = LBound(rngDB) To UBound(rngDB)
Debug.Print rngDB(i).Address
Debug.Print rngDB(i).Cells(1).Address
Debug.Print rngDB(i).Cells(1, 2).Address
Debug.Print rngDB(i).Cells(2, 1).Address
Next i
End Sub
Sub FindAll(text)
Dim fnd As String, FirstFound As String
Dim FoundCell As Range, rng As Range
Dim myRange As Range, LastCell As Range
Dim sAddress() As String
Dim n As Integer
fnd = text
Set myRange = ActiveSheet.UsedRange
Set LastCell = myRange.Cells(myRange.Cells.Count)
Set FoundCell = myRange.Find(what:=fnd, after:=LastCell)
'Test to see if anything was found
If Not FoundCell Is Nothing Then
FirstFound = FoundCell.Address
Else
'GoTo NothingFound
End If
Set rng = FoundCell
'Loop until cycled through all unique finds
Do
n = n + 1
ReDim Preserve rngDB(1 To n)
ReDim Preserve sAddress(1 To n)
Set rngDB(n) = FoundCell.CurrentRegion
sAddress(n) = rngDB(n).Address
Set FoundCell = myRange.FindNext(after:=FoundCell)
Loop While FoundCell.Address <> FirstFound
'Creates global value with all found adresses
If n Then
Values = Join(sAddress, ",")
End If
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
'...

Find a value, copy an offset but only to a point

In various places in column E of spreadsheet "Review" I have variables that start with the word "Sustainability:" (e.g., Sustainability: a, Sustainability:B"). Each time it finds one. I want it to copy the cell that is in the same row but two columns to the right. Then I want it to paste into a different sheet (SPSE Tran), starting at B63. Each time it pastes, the destination needs to offset by 1 row so it can paste down until it finds no more "Sustainability:". The code below is a start to this but I am stuck.
The second thing I need it to do (which I don't even know where to start) is to only iterate doing this until it finds a row that says "ONLY FOR TRANSITIONS". This leads into a new section that also includes "Sustainability:" but I don't want it to copy from there.
Thank you!
Sub SubmitData()
Dim RngA As Range
Dim FirstAd As String
Dim DestAd As Range
With Sheets("Review").Range("E:E")
Set RngA = .Find(What:="Sustainability:", lookat:=xlPart)
Set DestAd = Range("B63")
If Not RngA Is Nothing Then
FirstAd = RngA.Address
Do
Range(Cell, Cell.Offset(0, 2)).Copy _
Destination:=Sheets("SPSE Tran").Range(DestAd)
Set RngA = .FindNext(RngA)
Set DestAd = DestAd.Offset(0, 1)
Loop While Not RngA Is Nothing And RngA.Address <> FirstAd
End If
End With
End Sub
Here's your code revamped to use a filter instead of a find loop, and then it gets all the results and copies them to the destination at once:
Sub SubmitData()
Dim ws As Worksheet
Dim rngDest As Range
Dim rngStop As Range
With Sheets("SPSE Tran")
Set rngDest = .Cells(Rows.Count, "B").End(xlUp)
If rngDest.Row < 63 Then Set rngDest = .Range("B63")
End With
Set ws = Sheets("Review")
Set rngStop = ws.Columns("A").Find("ONLY FOR TRANSITIONS", , xlValues, xlPart)
With ws.Range("E1:E" & rngStop.Row)
.AutoFilter 1, "Sustainability:*"
.Offset(1, 2).Copy rngDest
.AutoFilter
End With
End Sub
How about (untested):
RngB = where you find "ONLY FOR TRANSITIONS"
RngBRow = RngB.Row
then change your Loop While .. to
Loop While Not RngA Is Nothing And RngA.Address <> FirstAd And RngA.Row < RngBRow

Excel VBA Macro: Copying relative cell to another worksheet

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

Resources