Using VBA to loop through different ranges and compare partial strings - excel

The problem I'm stuck with is as follows. I'm trying to cut "Price and Date" from an import page into a new table based on numerous criteria. I have a Specification sheet where I compare what those criteria have to be.
On the import page I have 3 criteria that have to be fulfilled first of all (these change based on the users input:
These are compared to a table which looks as follows: (This table doesn't change much. At most Origin and Key might be updated, or another row might be added)
For every line where Fruit, Type and Color match we have to look at another factor. Whether or not the fruit was bought from the "supermarket" or "farmer". On the import sheet we have the following table which changes every month.
When the fruit is bought at the Supermarket I want to use the correct key that corresponds with the row that fulfills the right criteria for "Fruit", "Type" and "Color". So in this example above I would like to use the key that corresponds with "Apple", "Fresh", "Red". Which in this example is just the first row. The corresponding key is "Supermarket ID 1" of which we have several rows of data in the import table. I would like to cut and paste the "Price" and "Date" from these rows into a new table.
For those fruits bought from the farmer it's a little different because 1) The comparable key is in a different column than the supermarket one and 2) The key is just a piece of the whole string of the import page (this is always the case). Here too I would like to cut the "Price" and "Date" into a different table.
Hopefully someone understands the problem. The code I've written so far is as follows:
Sub Fruits1()
Dim Criteria1 As Variant, Criteria2 As Variant, Criteria3 As Variant, Criteria4 As Variant, Criteria5 As Variant
Dim rng As Range, cell As Range
Dim wsImport As Worksheet: Set wsImport = Sheets("Import")
Dim wsSpec As Worksheet: Set wsSpec = Sheets("Specificaties")
Dim primarykey As String, comparingkey As String
Criteria1 = wsImport.Range("C3")
Criteria2 = wsImport.Range("C4")
Criteria3 = wsImport.Range("C5")
Set rng = wsSpec.Range("H3:H" & (wsSpec.Cells(Rows.Count, 8).End(xlUp).Row))
For Each cell In rng
If cell.Value = Criteria1 And cell.Offset(0, 1).Value = Criteria2 And cell.Offset(0, 2).Value = Criteria3 Then
If cell.Offset(0, 3) = "Supermarket" Then
import_lastrow = wsImport.Range("E" & Rows.Count).End(xlUp).Row
For i = import_lastrow To 2 Step -1
primarykey = cell.Offset(0, 4).Value
comparingkey = wsImport.Cells(i, 13).Value
If InStr(primarykey, comparingkey) > 0 Then
MsgBox "cut Price and Data into new table"
End If
Next i
ElseIf cell.Offset(0, 4) = "Farmer" Then
For i = import_lastrow To 2 Step -1
primarykey = cell.Offset(0, 4).Value
comparingkey = wsImport.Cells(i, 8).Value
If InStr(primarykey, comparingkey) > 0 Then
MsgBox "cut Price and Data into new table"
End If
Next i
End If
End If
Next cell
End Sub
The problem I believe lies in that I'm trying to loop through different ranges and not doing it right.

Logic:
Use .Find and .Findnext to search for 1st criteria. It is much faster than looping through every cell and matching the first criteria
Once you have your "Supermarket/Farmer" use Autofilter on the relevant column to identify and copy the relevant rows.
After copying, delete the unnecessary columns (if you wish)
Code:
Ok is this what you are trying? (UNTESTED). I quickly wrote this. Let me know if you get any errors?
Option Explicit
Dim wsImport As Worksheet
Sub Sample()
Dim wsSpec As Worksheet
Set wsImport = ThisWorkbook.Sheets("Import")
Set wsSpec = ThisWorkbook.Sheets("Specificaties")
Dim CriteriaA As String, CriteriaB As String, CriteriaC As String
Dim aCell As Range, bCell As Range
Dim origin As String, KeyToFind As String
With wsSpec
CriteriaA = .Range("C3").Value2
CriteriaB = .Range("C4").Value2
CriteriaC = .Range("C5").Value2
'~~> Using .Find to look for CriteriaA
Set aCell = .Columns(8).Find(What:=CriteriaA, LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
'~~> Check if found or not
If Not aCell Is Nothing Then
Set bCell = aCell
'~~> Secondary checks
If aCell.Offset(, 1).Value2 = CriteriaB And _
aCell.Offset(, 2).Value2 = CriteriaC Then '<~~ If match found
'~~> Get the origin and the key
origin = aCell.Offset(, 3).Value2
KeyToFind = aCell.Offset(, 4).Value2
Else '<~~ If match not found then search for next match
Do
Set aCell = .Columns(8).FindNext(After:=aCell)
If Not aCell Is Nothing Then
If aCell.Address = bCell.Address Then Exit Do
If aCell.Offset(, 1).Value2 = CriteriaB And _
aCell.Offset(, 2).Value2 = CriteriaC Then
origin = aCell.Offset(, 3).Value2
KeyToFind = aCell.Offset(, 4).Value2
Exit Do
End If
Else
Exit Do
End If
Loop
End If
'~~> Check the origin
If origin = "Supermarket" Then
CopyRows "F", KeyToFind, False
ElseIf origin = "Farmer" Then
CopyRows "H", KeyToFind, True
Else
MsgBox "Please check origin. Supermarket/Farmer not found. Exiting..."
End If
Else
MsgBox "Criteria A match was not found. Exiting..."
End If
End With
End Sub
'~~> Autofilter and copy filtered data
Private Sub CopyRows(Col As String, SearchString As String, PartialString As Boolean)
Dim copyFrom As Range
Dim lRow As Long
With wsImport
'~~> Remove any filters
.AutoFilterMode = False
lRow = .Range(Col & .Rows.Count).End(xlUp).Row
With .Range(Col & "1:" & Col & lRow)
If PartialString = False Then
.AutoFilter Field:=1, Criteria1:=SearchString
Else
.AutoFilter Field:=1, Criteria1:="=*" & SearchString & "*"
End If
Set copyFrom = .Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow
End With
'~~> Remove any filters
.AutoFilterMode = False
End With
'~~> Some sheet where you want to paste the output
Dim SomeSheet As Worksheet
Set SomeSheet = ThisWorkbook.Sheets("Output")
If Not copyFrom Is Nothing Then
'~~> Copy and paste to some sheet
copyFrom.Copy SomeSheet.Rows(1)
'After copying, delete the unwanted columns (OPTIONAL)
End If
End Sub

Related

Find specific text or dates inside a range and mark line with a specific colour

I have report were I use a VBA Macro to get a list from a large amount of Raw Data.
I have a very specific need, I hope someone can help me with.
My range is from A5:I500, each line from A:I has information to a specific need.
If a cell in the "H" Column has a specific text (in my case "Unconfirmed"), I would like The entire line (for instance A5:I5 or A26:I26), to be marked in a specific color.
If "H" Column has a date which is later than the date of "today", I would like the line (for instance A5:I5 or A26:I26), to be marked in a specific color.
If "H" Column has a date which is before the date of "today", I would like the line (for instance A5:I5 or A26:I26), to be marked in a specific color.
I want to end up like this
I found code which turns just the specific cell in the color I want.
How do I change this code to fill the entire Line from A:I on every line which contains "unconfirmed"?
Sub test1()
Dim FirstAddress As String
Dim MySearch As Variant
Dim myColor As Variant
Dim Rng As Range
Dim I As Long
MySearch = Array("Unconfirmed")
myColor = Array("3")
With Sheets("Ronnie").Range("A5:I1000")
For I = LBound(MySearch) To UBound(MySearch)
Set Rng = .Find(What:=MySearch(I), _
After:=.Cells(.Cells.Count), _
LookIn:=xlFormulas, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not Rng Is Nothing Then
FirstAddress = Rng.Address
Do
Rng.Interior.ColorIndex = myColor(I)
Set Rng = .FindNext(Rng)
Loop While Not Rng Is Nothing And Rng.Address <> FirstAddress
End If
Next I
End With
End Sub
This code will use if statements to check the data, and assign colour to the range of cells on that row.
Sub ColourRng()
Dim RNum As Integer
RNum = 1
For I = 1 To 500
If Sheets("Ronnie").Range("H" & RNum) = "Unconfirmed" Then
Sheets("Ronnie").Range("A" & RNum & ":I" & RNum).Interior.ColorIndex = 6
Else
If Sheets("Ronnie").Range("H" & RNum) >= Date Then
Sheets("Ronnie").Range("A" & RNum & ":I" & RNum).Interior.ColorIndex = 4
Else
If Sheets("Ronnie").Range("H" & RNum) < Date Then
Sheets("Ronnie").Range("A" & RNum & ":I" & RNum).Interior.ColorIndex = 3
End If
End If
End If
RNum = RNum + 1
Next I
End Sub

Highlighting Values In Column to Column Comparison using VBA

I am attempting to compare two columns in two separate sheets, each column contains data that is a string. My issue is that there is data in one column that is identical to the other in separate rows; therefore I have to check the entire column for the data before moving to the next. I am very inexperienced with VBA and am trying to make one portion of my job easier rather than comparing the columns by hand. I have piece wised the following code from research and trial and error. I am able to get the entire Column searched in my first Sheet, but only one value is being highlighted on the second sheet and then it is returning a value of "True" in the first column. I am unsure where I have gone wrong, any help is greatly appreciated!
Sub Better_Work_This_Time()
Dim FindString As String
Dim Rng As Range
ActiveCell = Sheets("Last Week").Range("A2").Activate
FindString = ActiveCell
Dim County As Integer
Count = Cells.CurrentRegion.rows.Count
For i = 2 To County
If Trim(FindString) <> "" Then
With Sheets("Current Week").Range("A:A")
Set Rng = .Find(What:=FindString, After:=.Cells(.Cells.Count), LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=True)
If Not Rng Is Nothing Then
ActiveCell.Font.Color = vbBlue
End If
End With
End If
If IsEmpty(FindString) Then
FindString = False
End If
ActiveCell.Offset(1, 0).Select
i = i + 1
Next
End Sub
Without using ActiveCell and using Match instead of Find.
Option Explicit
Sub Does_Work_This_Time()
Dim wb As Workbook, wsLast As Worksheet, wsCurrent As Worksheet
Dim FindString As String, ar, v
Dim LastRow As Long, i As Long, n As Long
Set wb = ThisWorkbook
' put current week values into array
Set wsCurrent = wb.Sheets("Current Week")
With wsCurrent
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
ar = .Range("A2:A" & LastRow).Value2
End With
' scan last week matching current week
Set wsLast = wb.Sheets("Last Week")
With wsLast
.Columns(1).Interior.Color = xlNone
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = 2 To LastRow
FindString = Trim(.Cells(i, "A"))
If Len(FindString) > 0 Then
v = Application.Match(FindString, ar, 0)
If IsError(v) Then
'no match
ElseIf ar(v, 1) = FindString Then ' case match
.Cells(i, "A").Interior.Color = RGB(128, 255, 128) ' light green
n = n + 1
End If
End If
Next
End With
MsgBox n & " rows matched"
End Sub

change first 3 characters to bold format

How do I change the first 3 characters and "CLEARANCE" Font to BOLD of cells containing "T##-" and loop it until the last row of STANDARD and NON-STANDARD tables
Sub Formatting()
Dim StartCell As Range
Set StartCell = Range("A15")
Dim myList As Range
Set myList = Range("A15:A" & Range("A" & Rows.Count).End(xlUp).Row)
Dim x As Range
For Each x In myList
'myList.ClearFormats
x.Font.Bold = False
If InStr(1, x.Text, "CLEARANCE") > 0 Or InStr(1, x.Text, "clearance") > 0 Then
x.Font.Bold = True
Else
x.Font.Bold = False
End If
Next
For Each x In myList
'myList.ClearFormats
x.Font.Bold = False
If InStr(1, x.Text, "T*") > 0 Then
x.Font.Bold = True
Else
x.Font.Bold = False
End If
Next
End Sub
ORIG
FORMATTED
Here is one way to achieve what you want which I feel is faster (I could be wrong). This way lets Excel do all the dirty work :D.
Let's say our data looks like this
LOGIC:
Identify the worksheet you are going to work with.
Remove any autofilter and find last row in column A.
Construct your range.
Filter the range based on "=T??-*" and "=*CLEARANCE*".
Identify the filtered range.
Check if there was anything filtered and if it was, then do a Find and Replace
Search for "CLEARANCE" and replace with bold tags around it as shown in the code.
Loop through the filtered range to create an html string and then copy to clipboard
Finally paste them back.
CODE:
Is this what you are trying? I have commented the code so you should not have a problem understanding it but if you do them simply ask :)
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim lRow As Long
Dim rng As Range, rngFinal As Range, aCell As Range
Dim htmlString As Variant
'~~> Set this to the relevant Sheet
Set ws = Sheet1
With ws
'~~> Remove any autofilter
.AutoFilterMode = False
'~~> Find last row in Col A
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
'~~> Construct your range
Set rng = .Range("A1:A" & lRow)
'~~> Filter the range
With rng
.AutoFilter Field:=1, Criteria1:="=T??-*", _
Operator:=xlAnd, Criteria2:="=*CLEARANCE*"
'~~> Set the filtered range
Set rngFinal = .Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow
End With
End With
'~~> Check if there was anything filtered
If Not rngFinal Is Nothing Then
rngFinal.Replace What:="CLEARANCE", Replacement:="<b>CLEARANCE</b>", _
LookAt:=xlPart, SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:= _
False, ReplaceFormat:=False, FormulaVersion:=xlReplaceFormula2
'~~> Loop through the filtered range and add
'~~> ending html tags and copy to clipboard and finally paste them
For Each aCell In rng.SpecialCells(xlCellTypeVisible)
If aCell Like "T??-*" Then
htmlString = "<html><b>" & _
Left(aCell.Value2, 4) & "</b>" & _
Mid(aCell.Value2, 5) & "</html>"
With CreateObject("htmlfile")
With .parentWindow.clipboardData
Select Case True
Case Len(htmlString): .setData "text", htmlString
Case Else: .GetData ("text")
End Select
End With
End With
DoEvents
aCell.PasteSpecial xlPasteAll
End If
Next aCell
End If
'~~> Remove any filters
ws.AutoFilterMode = False
End Sub
OUTPUT:
NOTE: If you want to bold either of the text when one of them is absent then change Operator:=xlAnd to Operator:=xlOr in the above code.
I thought I'd chuck in this solution based on regex. I was fiddling around a long time trying to use the Submatches attributes, but since they do not have the FirstIndex() and Lenght() properties, I had no other option than just using regular matching objects and the Like() operator:
Sub Test()
Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim rng As Range, cl As Range, lr As Long
lr = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Set rng = ws.Range("A1:A" & lr)
With CreateObject("vbscript.regexp")
.Global = True
.Pattern = "\bCLEARANCE\b"
For Each cl In rng
If cl.Value Like "T[0-9][0-9]-*" Then
cl.Characters(0, 3).Font.Bold = True
If .Test(cl.Value) Then
Set M = .Execute(cl.Value)
cl.Characters(M(0).firstindex + 1, M(0).Length).Font.Bold = True
End If
End If
Next
End With
End Sub
The Like() operator is there just to verify that a cell's value starts with a capital "T", two digits followed by an hyphen. This syntax is close to what regular expressions looks like but this can be done without a call to the regex-object.
When the starting conditions are met, I used a regex-match to test for the optional "CLEARANCE" in between word-boundaries to assert the substring is not part of a larger substring. I then used the FirstIndex() and Lenght() properties to bold the appropriate characters.
The short and easy, but not fast and flexible approach. "Bare minimum"
No sheet specified, so uses active sheet. Will ignore multiple instances of "CLEARANCE", will loop everything (slow), ingores starting pattern (only cares if it starts with "T"), doesn't remove any bold text from things that shouldn't be bold.
Sub FormattingLoop()
Dim x As Range
For Each x In Range("A15:A" & Cells(Rows.Count, "A").End(xlUp).Row)
If Left(x, 1) = "T" Then x.Characters(, 3).Font.FontStyle = "Bold"
If InStr(UCase(x), "CLEARANCE") > 0 Then x.Characters(InStr(UCase(x), "CLEARANCE"), 9).Font.FontStyle = "Bold"
Next x
End Sub

Find All Cells Containing string and Copy Adjacent

I am trying to search for all cells containing a specific string value. If possible multiple strings e.g.
"TextText*" that would find "123Text123Text123"
within a given range and return an ID reference from that row.
I managed to use an "If Cell.Value = xxx" scenario however this only looks for exact matches rather than containing:
intMyVal = InputBox("Please enter Sales Order No.")
lngLastRow = Cells(Rows.Count, "A").End(xlUp).Row
newrow = 1
For Each Cell In Range("D2:D" & lngLastRow) 'Data to search
If Cell.Value = intMyVal Then
Cells(Cell.Row, 1).Copy 'Copy ID1 value
Sheets("TempData").Cells(newrow, 1).PasteSpecial xlPasteValues 'Paste ID1 value in temp data
newrow = newrow + 1
End If
Next Cell
The below images shows an extract of the data. Column D would be searched for specific text strings (e.g. "Tesco" or "Ireland") and for each hit, the corresponding value in column A would be copied to a temporary data page.
Rather than look at each cell use FIND and FINDNEXT:
Public Sub FindSales()
Dim sValToFind As String
Dim rSearchRange As Range
Dim sFirstAdd As String
Dim rFoundCell As Range
Dim rAllFoundCells As Range
Dim sMessage As String
sValToFind = InputBox("Please enter Sales Order No.")
'Code to check a valid number entered
'.
'.
With ThisWorkbook.Worksheets("Sheet1")
Set rSearchRange = .Range(.Cells(1, 1), .Cells(.Rows.Count, 1).End(xlUp))
End With
With rSearchRange
Set rFoundCell = .Find(sValToFind, LookIn:=xlValues, LookAt:=xlPart)
If Not rFoundCell Is Nothing Then
sFirstAdd = rFoundCell.Address
Do
sMessage = sMessage & rFoundCell.Row & ", "
'Create a range of found cells.
If Not rAllFoundCells Is Nothing Then
Set rAllFoundCells = Union(rAllFoundCells, rFoundCell)
Else
Set rAllFoundCells = rFoundCell
End If
Set rFoundCell = .FindNext(rFoundCell)
Loop While rFoundCell.Address <> sFirstAdd
End If
End With
rAllFoundCells.Copy Destination:=ThisWorkbook.Worksheets("Sheet2").Range("A1")
sMessage = sValToFind & " found on rows " & Mid(sMessage, 1, Len(sMessage) - 2) & "."
MsgBox sMessage, vbOKOnly + vbInformation
End Sub
Solution is simple: use Like operator: If someCell.Value Like "*Text*Text*" Then would do exactly what you want.
In your case I suppose it would be:
If Cell.Value Like "*" & intMyVal & "*" Then

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
'...

Resources