Populate a column with input based on partial match from another column - excel

I have a sheet 1 where the column contains a list of domains e.g. www.nonsence.bg/sport, www.example.cn/streets, www.news.gr/sports
I would like to populate the Country input in column A based on the "KEY" table that is stored within the sheet 4 where column A = the domain (e.g. ".gr/", ".bg/", ".cn/") and column B = country (Greece, China, Bulgaria).
I use the following code for find/replace, but with the partial match it seems to be a bit more complex:
Sub substitute()
Dim Sh1 As Worksheet
Dim Sh2 As Worksheet
Dim FndList, x&
Set Sh1 = Sheets(1)
Set Sh2 = Sheets(3)
FndList = Sh2.Cells(1, 1).CurrentRegion
For x = 1 To UBound(FndList)
Sh1.Cells.Replace What:=FndList(x, 1), replacement:=FndList(x, 2), LookAt:=xlPart
Next
End Sub

Especially since your entries in KEY table are of the form xx/, it's pretty easy to do this with a formula:
I made the key table into a "real" table and am using structured references, but you can change that to normal references if you prefer
B2: =INDEX(tblKEY[Country], MATCH(TRUE,ISNUMBER(MATCH("*" & tblKEY[Domain]&"*",A2,0)),0))
KEY table
(named tblKEY)
Results
You can do the same thing with VBA and the Range.Find method, if you must use VBA for some other reason:
Option Explicit
Sub Country()
Dim wsSrc As Worksheet, LO As ListObject
Dim rSrc As Range, C As Range, CC As Range
'Change these to represent your actual locations
Set wsSrc = ThisWorkbook.Worksheets("sheet5")
Set rSrc = wsSrc.Cells(1, 1).CurrentRegion.Offset(rowoffset:=1)
Set LO = wsSrc.ListObjects("tblKEY")
Application.ScreenUpdating = False
rSrc.Columns(2).ClearContents
For Each CC In LO.DataBodyRange.Columns(1).Cells
With rSrc.Columns(1)
Set C = .Find(what:=CC.Value, LookIn:=xlValues, lookat:=xlPart, MatchCase:=False)
If Not C Is Nothing Then
C.Offset(0, 1) = CC.Offset(0, 1)
End If
End With
Next CC
End Sub

If I understood what you want correctly, this should work
Sub UpdateCountries()
Dim vCountriesList As Variant
Dim i As Long
Dim j As Long
Dim lLastRow As Long
With ThisWorkbook.Sheets("Sheet4")
vCountriesList = .Range(.Cells(2, "A"), .Cells(Rows.count, "B").End(xlUp)).Value
End With
With ThisWorkbook.Sheets("Sheet1")
lLastRow = .Cells(Rows.count, "H").End(xlUp).Row
For i = 2 To lLastRow
For j = 1 To UBound(vCountriesList, 1)
If InStr(1, .Cells(i, "H"), vCountriesList(j, 1), vbTextCompare) > 0 Then
If .Cells(i, "A").Value <> vCountriesList(j, 2) Then
.Cells(i, "A").Value = vCountriesList(j, 2)
End If
Exit For
End If
Next j
Next i
End With
End Sub

Related

Remove duplicate rows based on all columns via VBA

I found a great solution from this post: Removing duplicate rows after checking all columns
Sub Remove_DuplicateRows()
Dim intArray As Variant, i As Integer
Dim rng As Range
Dim ws As Worksheet
Call Open_Workbook
Set ws = Workbooks("Sales2021.xlsm").Sheets("Reporting Template")
ws.Activate
Set rng = ws.UsedRange.Rows
With rng
ReDim intArray(0 To .Columns.Count - 1)
For i = 0 To UBound(intArray)
intArray(i) = i + 1
Next i
.RemoveDuplicates Columns:=(intArray), Header:=xlYes
End With
End Sub
I tried the script, and wanted to adjust to my case: I want to delete all duplicated rows based on all columns except the first column (i.e., columns B to U). Should I use ws.Range("B2:U3000") instead of UsedRange?
You can either use ws.Range("B2:U3000") or below code
Set rng = ws.UsedRange.Offset(0, 1).Resize(ws.UsedRange.Rows.Count, ws.UsedRange.Columns.Count - 1)
The final code should look like this.
Sub Remove_DuplicateRows()
Dim intArray As Variant, i As Integer
Dim rng As Range
Dim ws As Worksheet
Call Open_Workbook
Set ws = Workbooks("Sales2021.xlsm").Sheets("Reporting Template")
ws.Activate
Set rng = ws.UsedRange.Offset(0, 1).Resize(ws.UsedRange.Rows.Count, ws.UsedRange.Columns.Count - 1)
With rng
ReDim intArray(0 To .Columns.Count - 1)
For i = 0 To UBound(intArray)
intArray(i) = i + 1
Next i
.RemoveDuplicates Columns:=(intArray), Header:=xlYes
End With
End Sub

VBA - Compare Sheet1 values to Sheet2, copy/paste the result to Sheet3

I'm trying to compare sheet1 "A" column values to sheet2 "E:E" column values and copy/paste the whole line of every match to sheet3. Please help me to complete this task. I'm very new to VBA.
Thank you very much in advance!
Sub DelDups_TwoLists()
Dim iListCount As Integer
Dim iCtr As Integer
' Turn off screen updating to speed up macro.
Application.ScreenUpdating = False
' Get count of records to search through (list that will be deleted).
iListCount = Sheets("sheet1").Cells(Rows.Count, "A").End(xlUp).Row
' Loop through the "master" list.
For Each x In Sheets("Sheet2").Range("E:E" & Sheets("Sheet1").Cells(Rows.Count, "A").End(xlUp).Row)
' Loop through all records in the second list.
For iCtr = iListCount To 1 Step -1
' Do comparison of next record.
' To specify a different column, change 1 to the column number.
If x.Value = Sheets("Sheet1").Cells(iCtr, 1).Value Then
' If match is true then delete row.
Sheets("Sheet1").Cells(iCtr, 1).EntireRow.Copy
Sheets("Sheet3").Select.Paste
End If
Next iCtr
Next
Application.ScreenUpdating = True
MsgBox "Done!"
End Sub
Sub DelDupsTwoLists()
Dim lastRowWs1 As Long, lastRowWs2 As Long
Dim ws1 As Worksheet, ws2 As Worksheet, ws3 As Worksheet
Set ws1 = Worksheets(1)
Set ws2 = Worksheets(2)
Set ws3 = Worksheets(3)
lastRowWs1 = LastRow(ws1.Name, 1)
lastRowWs2 = LastRow(ws2.Name, 5) 'E = 5
Dim myCell1 As Range, myCell2 As Range
Dim ws1Range As Range, ws2Range As Range
Set ws1Range = ws1.Range(ws1.Cells(1, "A"), ws1.Cells(lastRowWs1, 1))
Set ws2Range = ws2.Range(ws2.Cells(1, "E"), ws2.Cells(lastRowWs2, 1))
Dim rangeToDelete As Range
For Each myCell1 In ws1Range
For Each myCell2 In ws2Range
If myCell1.Value = myCell2.Value Then
Dim lastRowWs3: lastRowWs3 = LastRow(ws3.Name, 1) + 1
myCell2.EntireRow.Copy Destination:=ws3.Cells(lastRowWs3, 1)
If Not rangeToDelete Is Nothing Then
Set rangeToDelete = Union(rangeToDelete, myCell2.EntireRow)
Else
Set rangeToDelete = myCell2.EntireRow
End If
End If
Next
Next
If Not rangeToDelete Is Nothing Then
Debug.Print "Deleting rangeToDelete - "; rangeToDelete.Address
rangeToDelete.Delete
End If
Debug.Print "Done!"
End Sub
Public Function LastRow(wsName As String, Optional columnToCheck As Long = 1) As Long
Dim ws As Worksheet
Set ws = Worksheets(wsName)
LastRow = ws.Cells(ws.Rows.Count, columnToCheck).End(xlUp).Row
End Function
Pretty much I rewrote the whole code from scratch. It pretty much uses the initial n2 complexity, but is rather faster than that, because the deletion of the rows in WorkSheet(2) is done in a single last step rangeToDelete.Delete, which saves a lot of time.
Pretty much, the code defines 2 ranges with which is works - ws1Range and ws2Range, using the LastRow function. Once it defines them, it starts looping through them and comparing them. Hence the n2 complexity. In case of equal values, the row is copied and the cell is added to the rangeToDelete.
Note - it will probably not work as "out of the box solution", but try to debug further with F8 and see what happens.
Additionally:
Using Integer is not a great idea in VBA.
"_" in the Sub name is used for Events in VBA, thus it is not a great idea to use it. (although it works)
How to avoid using Select in Excel VBA
Give this a try (see comments in code for more details):
Sub DelDups_TwoLists()
' Turn off screen updating to speed up macro.
Application.ScreenUpdating = False
With ActiveWorkbook
Dim wsSrc As Worksheet: Set wsSrc = .Sheets("Sheet1") 'declare and set the source worksheet
Dim wsDst As Worksheet: Set wsDst = .Sheets("Sheet3") 'declare and set the destination worksheet
Dim R1 As Long, R2 As Long, C As Long, lRow As Long, lCol As Long 'declare variables to use
With wsSrc
lCol = .Cells(1, Columns.Count).End(xlToLeft).Column 'get the last column value in the source sheet, at row 1, will reuse this laster
Dim arrData_1 As Variant: arrData_1 = .Range(.Cells(1, 1), .Cells(.Cells(Rows.Count, 1).End(xlUp).Row, 1)) 'declare and allocate the source data to an array
End With
With .Sheets("Sheet2")
Dim arrData_2 As Variant: arrData_2 = .Range("E1:E" & .Cells(Rows.Count, 1).End(xlUp).Row) 'declare and allocate the compare data to an array
End With
End With
With wsDst
For R1 = LBound(arrData_1) To UBound(arrData_1) 'for each row in the source data
For R2 = LBound(arrData_2) To UBound(arrData_2) 'for each row in the compare data
If arrData_1(R1, 2) = arrData_2(R2, 1) Then 'if there is a match
lRow = .Cells(Rows.Count, 1).End(xlUp).Row + 1 'get the last row in the destination sheet
.Range(.Cells(lRow, 1), .Cells(lRow, lCol)).Value = _
wsSrc.Range(wsSrc.Cells(R1, 1), wsSrc.Cells(R1, lCol)).Value 'allocate the matching values
Exit For 'exit early here if there is a match, go to next row to check
End If
Next R2
Next R1
End With
Application.ScreenUpdating = True
MsgBox "Done!"
End Sub

VBA clear cells adjacent to the column if it is empty

I have 5 columns. If column 3 has no value, I want all other adjacent cells (column 1,2,4,5) to clear.
I got this from another site:
Sub ClearCust()
'Clears data in column if there is no Amt number next to it.
'Used in conjunction to fill blanks.
Dim j As Range
For Each j In Workbooks("OH Details_v1").Worksheets("Sheet1").Range("C2:D" & Worksheets("Sheet1").Range("a65536").End(xlUp).Row)
If j.Value = 0 Then
j.Offset(0, 1).ClearContents
End If
Next j
End Sub
But it only clears column C, D, E...
Something like this might be what you're looking for:
Sub ClearCust()
Dim wb As Workbook
Dim ws As Worksheet
Dim rLast As Range
'Set wb = Workbooks("OH Details_v1")
Set wb = ThisWorkbook
Set ws = wb.Worksheets("Sheet1")
Set rLast = ws.Range("A:E").Find("*", ws.Range("A1"), xlValues, , , xlPrevious)
If rLast Is Nothing Then Exit Sub 'No data
With ws.Range("C1:C" & rLast.Row)
.AutoFilter 1, "="
Intersect(.Parent.Range("A:E"), .Offset(1).EntireRow).ClearContents
.AutoFilter
End With
End Sub
EDIT:
To address your request of iterating over sets of columns to perform this same task, you can do something like this:
Sub ClearCust()
Dim wb As Workbook
Dim ws As Worksheet
Dim rLast As Range
Dim aClearAreas() As String
Dim i As Long
'Set wb = Workbooks("OH Details_v1")
Set wb = ThisWorkbook
Set ws = wb.Worksheets("Sheet1")
ReDim aClearAreas(1 To 3, 1 To 2)
'Define columns that will be cleared Define column within that range to evaluate for blanks
aClearAreas(1, 1) = "A:E": aClearAreas(1, 2) = "C"
aClearAreas(2, 1) = "F:J": aClearAreas(2, 2) = "H"
aClearAreas(3, 1) = "K:O": aClearAreas(3, 2) = "M"
'loop through your array that contains your clear area data
For i = LBound(aClearAreas, 1) To UBound(aClearAreas, 1)
'Get last populated row within the defined range
Set rLast = ws.Range(aClearAreas(i, 1)).Find("*", ws.Range(aClearAreas(i, 1)).Cells(1), xlValues, , , xlPrevious)
If Not rLast Is Nothing Then
'Filter on the column to be evaluated
With ws.Range(aClearAreas(i, 2) & "1:" & aClearAreas(i, 2) & rLast.Row)
.AutoFilter 1, "=" 'Filter for blanks
Intersect(.Parent.Range(aClearAreas(i, 1)), .Offset(1).EntireRow).ClearContents 'Clear cells only in the defined range
.AutoFilter 'Remove the filter
End With
End If
Next i
End Sub
Your explanation and title are two different subjects but based on your explanation-i understand you want to loop through column C and if a cell is empty, then you make other cells value to blank-i wrote below code. You may use. Tested
Sub test()
Dim ws As Worksheet
Set ws = ActiveSheet
With ws
lr = .Cells(Rows.Count, "C").End(xlUp).Row
For i = 2 To lr
If .Cells(i, "C") = "" Then
.Cells(i, "A") = ""
.Cells(i, "B") = ""
.Cells(i, "D") = ""
.Cells(i, "E") = ""
End If
Next i
End With
End Sub

VBA - Vlookup Multiple Columns and Fill to End of Range

I need to do a Vlookup of an ID on the source sheet to a table in the data sheet. When the Vlookup is done, it needs to return the cell values from 6 different columns.
Here I have a function to get the range:
Function find_Col(header As String) As Range
Dim aCell As Range, rng As Range, def_Header As Range
Dim col As Long, lRow As Long, defCol As Long
Dim colName As String, defColName As String
Dim y As Workbook
Dim ws1 As Worksheet
Set y = Workbooks("Template.xlsm")
Set ws1 = y.Sheets("Results")
With ws1
Set def_Header = Cells.Find(what:="ID", LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False, SearchFormat:=False)
Set aCell = .Range("B2:Z2").Find(what:=header, LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False, SearchFormat:=False)
If Not aCell Is Nothing Then
defCol = def_Header.Column
defColName = Split(.Cells(, defCol).Address, "$")(1)
col = aCell.Column
colName = Split(.Cells(, col).Address, "$")(1)
lRow = Range(defColName & .Rows.count).End(xlUp).Row - 1
Set myCol = Range(colName & "2")
'This is your range
Set find_Col = Range(myCol.Address & ":" & colName & lRow).Offset(1, 0)
'If not found
Else
MsgBox "Column Not Found"
End If
End With
End Function
Then in my sub, I select the range and do a Vlookup which fills this range:
Selection.FormulaR1C1 = "=VLOOKUP(RC[-4],myTable,2,FALSE)"
And this works great.
Then I needed to return more than just one column, so I ended up with the formula:
Selection.FormulaArray = "=VLOOKUP($C3,myTable,{2,3,4,5,6},FALSE)"
Source Sheet:
Data Sheet:
So, my function returns only the range for one column, which I think I can use in terms of getting a row count then using something like this:
Set myRng = find_Col("Product")
For currentRow = myRng.Rows.count To 1 Step -1
Selection.FormulaArray = "=VLOOKUP($C3,myTable,{2,3,4,5,6},FALSE)"
Next currentRow
Then perhaps instead of C3 it could look something like this:
C & currentRow --> Selection.FormulaArray = "=VLOOKUP($C & currentRow,myTable,{2,3,4,5,6},FALSE)"
But then I have the issue that only one cell is selected (G3) and from H-L is not. And I have no idea whether this is even a plausible effort.
Ideally of course, I would have cells G3:L3 selected and fill the formula down to the last row.
My brain is just fried from all the thinking and attempts.
So this should do the trick... I've explained every instance but if you need help understanding just ask:
Option Explicit
Sub FillData1()
Dim ws As Worksheet, wsData As Worksheet, arr As Variant, arrData As Variant
Dim DictHeaders As Scripting.Dictionary, DictIds As Scripting.Dictionary, DictDataHeaders As Scripting.Dictionary, _
DictDataIds As Scripting.Dictionary
Dim LastRow As Long, LastCol As Integer, i As Long, j As Integer
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
With ThisWorkbook
Set ws = .Sheets("Results")
Set wsData = .Sheets("List")
End With
'Lets suppose your data always starts on row 2 in both sheets and column B will always have the max amount of rows filled
With ws 'filling the first array
LastRow = .Cells(.Rows.Count, 2).End(xlUp).Row
LastCol = .Cells(2, .Columns.Count).End(xlToLeft).Column
arr = .Range("B2", .Cells(LastRow, LastCol)).Value
End With
With wsData 'filling the data array
LastRow = .Cells(.Rows.Count, 4).End(xlUp).Row
LastCol = .Cells(2, .Columns.Count).End(xlToLeft).Column
arrData = .Range("A2", .Cells(LastRow, LastCol)).Value
End With
'Now lets put everything into Dictionaries so if the data moves columns or rows won't matter
Set DictHeaders = New Scripting.Dictionary
Set DictIds = New Scripting.Dictionary
For i = 1 To UBound(arr, 2) 'this will fill the headers positions on the main sheet
If Not DictHeaders.Exists(arr(1, i)) Then DictHeaders.Add arr(1, i), i
Next i
For i = 2 To UBound(arr, 1) 'this will fill the IDs positions on the main sheet
If Not DictIds.Exists(arr(i, DictHeaders("KW ID"))) Then DictIds.Add arr(i, 1), i
Next i
Set DictDataHeaders = New Scripting.Dictionary
Set DictDataIds = New Scripting.Dictionary
For i = 1 To UBound(arrData, 2) 'this will fill the headers positions on the data sheet
If Not DictDataHeaders.Exists(arrData(1, i)) Then DictDataHeaders.Add arrData(1, i), i
Next i
For i = 2 To UBound(arrData, 1) 'this will fill the IDs positions on the data sheet
If Not DictDataIds.Exists(arrData(i, DictDataHeaders("KW ID"))) Then DictDataIds.Add arrData(i, DictDataHeaders("KW ID")), i
Next i
'Finally will loop through the main array to fill it with the data from the data array
On Error Resume Next
For i = 2 To UBound(arr)
For j = 6 To UBound(arr, 2) 'I'm assuming you want to avoid the first columns which are hidden
arr(i, j) = arrData(DictDataIds(arr(i, 1)), DictDataHeaders(arr(1, j)))
Next j
Next i
On Error GoTo 0
With ws 'filling the first array
LastRow = .Cells(.Rows.Count, 2).End(xlUp).Row
LastCol = .Cells(2, .Columns.Count).End(xlToLeft).Column
.Range("B2", .Cells(LastRow, LastCol)).Value = arr
End With
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
I don't know if I got the true issue of your goal. However, since your Selection parts in your code should be avoid, why don't make something like the following?
Set myRng = find_Col("Product")
For currentRow = myRng.Rows.count To 1 Step -1
Range(Cells(currentRow, 5), Cells(currentRow, 9)).FormulaArray = "=VLOOKUP(RC3,myTable,{2,3,4,5,6},FALSE)"
Next currentRow

Format number to be able to search properly

I am trying to format some numbers where some have a leading zero so that I can then search them.
I am needing to format a set of numbers where all are 6 digits and some have a leading zero. I then have a separate code search those numbers for a specific one so the resulting format needs to be searchable. The first code below is the formatting I can't figure out and then the search code. If I simply do an "000000" for formatting I don't believe it works for my search anymore as those now become Special format. Help please?
Sub (First Code)
Dim lngLastRow As Long
lngLastRow = Cells(Rows.Count, "A").End(xlUp).Row
Range("O2:P" & lngLastRow).Select 'specify the range which suits your purpose
With Selection
Selection.NumberFormat = "General"
.Value = .Value
End With
Dim SUPLCD As Range
Set SUPLCD = Range("Q2:Q")
With Selection
Selection.NumberFormat = "#"
Selection.Value = Format(Selection, "000000")
End With
End Sub
Sub Worksheet()
Dim i As Long
Dim j As Long
Dim wsCurrent As Worksheet
Set wsCurrent = ActiveSheet
Dim wsData As Worksheet
Dim rngData As Range
Set wsData = ThisWorkbook.Worksheets("Tempinterior")
Dim wsTempinterior As Worksheet
' Note that .Add will activate the new sheet so we'll
' need to reactivate the worksheet that was previously active
Set wsTempinterior = Worksheets.Add
wsTempinterior.Name = "copy"
' Find the used range in columns A to K and copy over starting
' at cell A1 of wsGalreq
Set rngData = Intersect(wsData.UsedRange, wsData.Range("A:M"))
' Copy over the first row containing column headers
j = 1
rngData.Rows(1).Copy Destination:=wsTempinterior.Cells(j, 1)
For i = 2 To rngData.Rows.Count
' Check cell of column 10 of row i and copy if matched
If rngData.Cells(i, 10).Value = "026572" Or rngData.Cells(i, 10).Value = "435740" Or rngData.Cells(i, 10).Value = "622639" Then
' Copy over to wsDalreq from row j
j = j + 1
rngData.Rows(i).Copy Destination:=wsTempinterior.Cells(j, 1)
End If
Next
End Sub
With above code, the search doesn't pull the entries with those numbers I think because they are formatted as Special.
You don't have to format Col Q to add a 0, you can accomplish your task with out formatting by using Like in your If statement. Because you are not clear about where the values are, you are formatting Col Q but searching Col J, I used Col Q.
Dim wsData As Worksheet
Set wsData = ThisWorkbook.Worksheets("Sheet1") '("Tempinterior")
Dim rngData As Range
Set rngData = Intersect(wsData.UsedRange, wsData.Range("A:M"))
Sheets.Add(After:=Sheets(Sheets.Count)).Name = "copy"
j = 1
rngData.Rows(1).Copy Destination:=Sheets("copy").Cells(j, 1) 'copy headers for rngData
For i = 2 To rngData.Rows.Count
If wsData.Cells(i, 17).Value Like "26572" Or Sheet1.Cells(i, 17).Value = "435740" Or _
Sheet1.Cells(i, 17).Value = "622639" Then
j = j + 1
rngData.Rows(i).Copy Destination:=Sheets("Copy").Cells(j, 1)
End If
Next i
End Sub
First avoid .Select and you will need to loop the change:
Sub first()
Dim lngLastRow As Long
With Worksheets("Sheet1") 'Change to your sheet
lngLastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
With .Range("O2:P" & lngLastRow) 'specify the range which suits your purpose
.NumberFormat = "General"
.Value = .Value
End With
Dim SUPLCD As Range
Set SUPLCD = .Range("Q2:Q" & lngLastRow)
Dim rng As Range
For Each rng In SUPLCD
rng.NumberFormat = "#"
rng.Value = Format(rng.Value, "000000")
Next rng
End With
End Sub

Resources