Find cell(s) with certain text and add hyperlinks in a loop - excel

tldr: Find cell(s) with part number xxxxx and add hyperlink to drawing on server.
We have a spreadsheet containing part numbers across multiple columns & rows. Our requirement is to add a hyperlink to parts' drawing, stored on our server. We have tried highlighting them as a group, but get the error
this can't be done on multiple range selection
We also want to keep the comment information intact, just to complicate it further.
Is there code we can use to search for part number xxxxx & add a hyperlink, then find the next cell and repeat the process?
We have found a "find all" code which highlights the cells, just need some help with the hyperlink issue.
Sub FindAll()
Dim fnd As String, FirstFound As String
Dim FoundCell As Range, rng As Range
Dim myRange As Range, LastCell As Range
'What value do you want to find (must be in string form)?
fnd = "70005"
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
'Select Cells Containing Find Value
rng.Select
Exit Sub
'Error Handler
NothingFound:
MsgBox "No values were found in this worksheet"
End Sub

Your method can be simplified a bit. My suggestion is to create a function that will add your hyperlinks to any given area.
My test data is
Option Explicit
Sub test()
Dim linkCount As Long
linkCount = AddHyperLinkTo(FindArea:=Sheet1.UsedRange, _
FindThis:="red", _
Link:="https://google.com")
Debug.Print "found: " & linkCount
End Sub
Function AddHyperLinkTo(ByRef FindArea As Range, _
ByVal FindThis As Variant, _
ByVal Link As String) As Long
Dim numberFound As Long
Dim parentWS As Worksheet
Set parentWS = FindArea.Parent
Dim firstFind As Range
Dim findResult As Range
Set findResult = FindArea.Find(What:=FindThis, LookIn:=xlValues)
Set firstFind = findResult
Do Until findResult Is Nothing
parentWS.Hyperlinks.Add Anchor:=findResult, Address:=Link
numberFound = numberFound + 1
Set findResult = FindArea.Find(What:=FindThis, LookIn:=xlValues, After:=findResult)
If findResult.Address = firstFind.Address Then
Exit Do
End If
Loop
AddHyperLinkTo = numberFound
End Function

Related

Search range for all cells with specific text and change the value of all adjacent cell to 0

Looking for help to achieve searching a range of cells E9:E with All cells containing "Accommodation & Transportation" and changing the value of the cells adjacent to them with 0. , I was not able to get anything online with similar topic and I'm not too good with VBA coding, though i am able to understand what the code will provide in results.
I Have a Commandbutton1 with the below code :
Sub CommandButton1_click()
Dim blanks As Excel.Range
Set blanks = Range("F9:F" & Cells(Rows.Count, 5).End(xlUp).Row).SpecialCells(xlCellTypeBlanks)
blanks.Value = blanks.Offset(0, -1).Value
End Sub
Further i have a command button that will select only cells that are not blank. I need the above result because if the below code selects Non Blank cells from Columns E:F it wont be selecting cells adjacent to those containing "Accommodation & Transportation" as they are blank cells and it will return the error "Runtime Error '1004' This action wont work on multiple selections".
The below code acts the same as [Go to Special => Constants]
Sub SelectNonBlankCells()
Dim rng As Range
Dim OutRng As Range
Dim InputRng As Range
Dim xTitle As String
On Error Resume Next
xTitle = Application.ActiveWindow.RangeSelection.Address
Set InputRng = Range("E8:F500")
ActiveWindow.ScrollRow = 1
For Each rng In InputRng
If Not rng.Value = "" Then
If OutRng Is Nothing Then
Set OutRng = rng
Else
Set OutRng = Application.Union(OutRng, rng)
End If
End If
Next
If Not (OutRng Is Nothing) Then
OutRng.Select
End If
End Sub
Maybe you can try another approach, if your goal is to edit cells adjacent to certain cells. The code below is based on an example in the Help file of the Range.Find function:
Sub DoSomething()
Dim sh As Worksheet
Set sh = ActiveSheet
Dim checkRange As Range
Set checkRange = sh.Range("E8:F500") ' your intended range to search
Dim foundRange As Range
Set foundRange = checkRange.Find("Accommodation & Transportation")
Dim firstAddr As String
If Not foundRange Is Nothing Then
firstAddr = foundRange.Address
Do
' use foundRange to access adjacent cells with foundRange.Offset(row, col)
'
'
foundRange.Offset(0, 1) = "all good"
Set foundRange = checkRange.FindNext(foundRange)
Loop While Not foundRange Is Nothing And foundRange.Address <> firstAddr
End If
End Sub
Or even better, you could add some parameters to make it more reusable:
Sub Main()
DoSomething "Accommodation & Transportation", ActiveSheet.Range("E8:F500")
End Sub
Sub DoSomething(ByVal findWhat As String, ByVal searchWhere As Range)
Dim foundRange As Range
Set foundRange = searchWhere.Find(findWhat)
Dim firstAddr As String
If Not foundRange Is Nothing Then
firstAddr = foundRange.Address
Do
' use foundRange to access adjacent cells with foundRange.Offset(row, col)
'
'
foundRange.Offset(0, 1) = "all good"
Set foundRange = searchWhere.FindNext(foundRange)
Loop While Not foundRange Is Nothing And foundRange.Address <> firstAddr
End If
End Sub

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

How to locate the last row of cells in a range which matches using VBA?

There is one column in a table where names of factories are shown but I only need the data for a specific factory name(let's say factory "Australia").
My idea is to locate the first and last rows which match because the data for the same factory are always presented in a consecutive manner. In this way, I can get the range of cells which match up to my search.
Locating the first matched row position is quite easy but I get stuck in getting the last matched row position.
Here is the code regarding this section:
Sub Search()
Dim sh As Worksheet
Dim searchStr As String
Dim lastRow As Long, firstRow as Long
Dim tableRange As range
Set sh = Worksheets("Total order")
searchStr = "Australia"
Set tableRange = sh.range("B:B").Find(what:=searchStr, LookIn:=xlValues, lookat:=xlWhole)
firstRow = tableRange.Row
End Sub
An example of the table dealt with:
Refer to the Range from the Cell of the First to the Cell of the Last Occurrence of a String in a Column
A Side Note
The Range.Find method is kind of tricky. For example, you may not be aware that in your code the search starts from cell B2 (which is even preferable in this case), and using xlValues may result in undesired results if rows are hidden (probably not important).
Usage
Using the function, according to the screenshot, you could (after searchStr = "Australia") use:
Set tableRange = refRangeFirstLast(sh.Columns("B"), searchStr)
to refer to the range B4:B7, or use:
Set tableRange = refRangeFirstLast(sh.Columns("B"), searchStr).Offset(, -1).Resize(, 4)
to refer to the range A4:D7.
The Code
Function refRangeFirstLast( _
ByVal ColumnRange As Range, _
ByVal SearchString As String) _
As Range
If Not ColumnRange Is Nothing Then
With ColumnRange
Dim FirstCell As Range: Set FirstCell = _
.Find(SearchString, .Cells(.Cells.Count), xlFormulas, xlWhole)
If Not FirstCell Is Nothing Then
Dim LastCell As Range: Set LastCell = _
.Find(SearchString, , xlFormulas, xlWhole, , xlPrevious)
Set refRangeFirstLast = .Worksheet.Range(FirstCell, LastCell)
End If
End With
End If
End Function
Sub refRangeFirstLastTEST()
Const SearchString As String = "Australia"
Dim ColumnRange As Range
Set ColumnRange = ThisWorkbook.Worksheets("Total order").Columns("B")
Dim rg As Range: Set rg = refRangeFirstLast(ColumnRange, SearchString)
If Not rg Is Nothing Then
Debug.Print rg.Address
Else
MsgBox "The reference could not be created.", vbExclamation, "Fail?"
End If
End Sub

Excel VBA - For Each Loop Alternative

I have a For Each Loop that looks for cells that contain a string with a wildcard and if that string is not bold. If those conditions are met then that cell's row is deleted. I believe the For Each Loop is inefficient, and even with only around 200 rows the code takes a few seconds to run. Is there a more efficient way to achieve these results?
Dim Cell As Range
Dim sheetRange As Range
Set sheetRange = ActiveSheet.UsedRange
For Each Cell In sheetRange
Set Cell = sheetRange.Find(What:="Total*", lookat:=xlPart)
If Not Cell Is Nothing Then
If Cell.Font.Bold = False Then
Cell.EntireRow.Delete
End If
End If
Next Cell
Please take a look at the code below and see if you can adapt it to your specific use case. The DeleteTotalRows subroutine uses the built-in .Find method to jump specifically to cells that include the value 'Total'. It passes each of these cells to the MergeDeleteRange subroutine. This sub will build a range to delete, which contains all rows with the Total word and bold font.
Report back if you run into issues.
Option Explicit
Sub DeleteTotalRows()
Dim fnd As Range
Dim rngToDelete As Range
Dim firstFnd As Range
Dim sht As Worksheet
'Update this
Set sht = Worksheets("Sheet2")
With sht
Set fnd = .Cells.Find(what:="Total", lookat:=xlPart)
If fnd Is Nothing Then Exit Sub
Set firstFnd = fnd
Do
MergeDeleteRange rngToDelete, fnd
Set fnd = .Cells.Find(what:="Total", lookat:=xlPart, after:=fnd)
Loop While fnd.Address <> firstFnd.Address
End With
If rngToDelete Is Nothing Then Exit Sub
rngToDelete.Delete
End Sub
Private Sub MergeDeleteRange(ByRef outputRng As Range, ByRef inputCell As Range)
'Not deleting if the cell isn't bold
If Not inputCell.Font.Bold Then Exit Sub
'Create output range if it's still empty
If outputRng Is Nothing Then Set outputRng = inputCell.EntireRow
'Since you are testing multiple columns, confirm that the
'row isn't already in the output range
If Not Intersect(inputCell, outputRng) Is Nothing Then
Exit Sub
End If
Set outputRng = Union(outputRng, inputCell.EntireRow)
End Sub

VBA - update find function to loop through rows and move on if value isn't there

Trying to put together a macro that searches each row to see if it contains 7 search terms (see "Warranty:" example below). If the cell starts with one of the phrases (like "Warranty:"), then that cell is pasted in a specific cell (same row but different column) in another worksheet.
Issues:
Had trouble with the macro until I added the select function - I know this slows them down, but I couldn't figure out a way to do this without it
Can't figure out how to get it to loop through all rows
Errors if the row doesn't have the word - need it to just keep going through
Sub FindTest()
Worksheets("Macro").Range("1:1").Find(What:="Warranty: ", LookIn:=xlValues, LookAt:=xlPart, MatchCase:=True).Copy
'Cell begins with "Warranty:" but text following varies
Sheets("CSV Upload").Select
Sheets("CSV Upload").Range("J1").Select
ActiveSheet.Paste
End Sub
UPDATE:
Sub FindTest()
Dim Macro As Worksheet: Set Macro = Sheets("Macro")
Dim CSV As Worksheet: Set CSV = Sheets("CSV Upload")
'On Error Resume Next
For R = 1 To Macro.UsedRange.Rows.Count
Set rng = Macro.Rows(R)
Dim FindRange As Range: Set FindRange = rng.Find(What:="Warranty:", LookIn:=xlValues, LookAt:=xlPart, MatchCase:=True)
'FindRange.Copy CSV.Range("J1")
'CSV.Cells(1, J) = Macro.Cells(FindRange)
Next
'On Error GoTo 0
End Sub
To loop through each row in the worksheet:
Dim ws As Worksheet: Set ws = Sheets("Macro")
Dim csv_upload As workseet: Set csv_upload = Sheets("CSV Upload")
For r = 1 To ws.UsedRange.Rows.Count
Set rng = ws.Rows(r)
rng.Find(What:="Warranty: ", LookIn:=xlValues, LookAt:=xlPart, MatchCase:=True)
...
Next
Then to copy the values, depending on which cells you need to copy
csv_upload.cells(dest_row, dest_col) = ws.cells(orig_row, orig_col)
For it to continue when you have an error, you can tell it to resume
On Error Resume Next
' potential for error to be raised
' Don't use this unless you know you are going to get a specific
' error and know there are no unintended consequences of ignoring it.
On Error GoTo 0
Using the code in your update, the following code should work for you.
Sub FindWarranty()
Dim Macro As Worksheet: Set Macro = Sheets("Macro")
Dim CSV As Worksheet: Set CSV = Sheets("CSV Upload")
Dim rng As Range, FindRange As Range
Dim Phrase As String
Phrase = "Warranty:"
For r = 1 To Macro.UsedRange.Rows.Count
Set rng = Macro.Rows(r)
Set FindRange = rng.Find(What:=Phrase, LookIn:=xlValues, LookAt:=xlPart, MatchCase:=True)
If Not FindRange Is Nothing Then
' Set destination cell to what you need it to be
c = 1
CSV.Cells(r, c) = FindRange
End If
Next
End Sub
A slightly more elegant way that Quicksilver alluded to is:
Sub FindWarrantys()
Dim Macro As Worksheet: Set Macro = Sheets("Macro")
Dim CSV As Worksheet: Set CSV = Sheets("CSV Upload")
Dim FoundCell As Range, FirstAddr As String
Dim Phrase As String, c As Integer
Phrase = "Warranty:"
' Find the first occurrence. The after variable is set to the
' last cell so that it will start searching from the beginning.
Set FoundCell = Macro.UsedRange.Find(what:=Phrase, _
after:=Macro.UsedRange.Cells(Macro.UsedRange.Cells.Count))
' Save the address of the first occurrence to prevent an infinite loop
If Not FoundCell Is Nothing Then
FirstAddr = FoundCell.Address
End If
' Loop through all finds
Do Until FoundCell Is Nothing
c = 1 ' Adjust for logic to determine which column
CSV.Cells(FoundCell.Row, c) = FoundCell
' Find the next occurrence
Set FoundCell = Macro.UsedRange.FindNext(after:=FoundCell)
' Break if we're back at the first address
If FoundCell.Address = FirstAddr Then
Exit Do
End If
Loop
End Sub

Resources