Find a matching suburb in an address - excel

I have an Excel sheet with a list of addresses on each line
i.e.
COLUMN A
My Company 123 Big Street Ashgrove QLD 4111
A Better Compant PO Box 123 Sandgate QLD 4111
I have another sheet with every QLD suburb in it in alphabetical order in a named range called rSuburbs
i.e.
Ashgrove
BBBB
CCC
Sandgate
Zilmere
What formula can I write to find the closest match and dump it out, i.e. like this:
COLUMN A COLUMN B
My Company 123 Big Street Ashgrove QLD 4111 Ashgrove
A Better Compant PO Box 123 Sandgate QLD 4111 Sandgate

Try this formula in B2 copied down
=LOOKUP(2^15,SEARCH(" "&rSuburbs&" "," "&A2&" "),rSuburbs)
Using " "& ensures that you don't get partial matches

Assuming your list of suburbs is in column K2:6 with the heading suburbs in K1:
{=INDEX(K:K,LARGE(IFERROR(FIND($K$2:$K$6,A2)*0+ROW(A$2:A$6),0),1))}
Array formulas must be confirmed with ctrl+shift+enter -- do not try to enter the squiggly braces manually!
This will return the desired output.
Basically, figure out if each of the list is in the text in column A, return the row number of the suburb if it is, or zero if it isn't, and take the suburb of the largest index you get.

If you are interested in a vba solution this will print the suburb name in Column B, amend to match your workbook.
Option Explicit
Sub splitlr()
Dim wb As Workbook
Dim ws As Worksheet
Dim s As String, str As String
Dim i As Integer, j As Integer, k As Integer, l As Integer
Dim lr As Long
Dim a As Long
Set wb = ThisWorkbook
Set ws = wb.Sheets("Sheet1")
lr = ws.Range("A" & Rows.Count).End(xlUp).Row + 1
With ws
a = 1
Do Until a = lr
s = .Range("A" & a).Text
i = Len(s)
j = InStrRev(s, " ") - 1
k = InStrRev(s, " ", j) - 1
l = InStrRev(s, " ", k)
str = Mid(s, l, (i - k))
.Range("B" & a).Value = str
a = a + 1
Loop
End With
End Sub

Related

How to create a loop that populates a variable-sized matrix with COUNTIFS?

It's rather a complex situation. I have a routine that needs to be done every other day. I have a workbook with 2 different sheets, one called "deals list", contains a table like this:
Salesman
Campaign
Name 1
Campaign A
Name 1
Campaign B
Name 2
Campaign C
Name 3
Campaign A
Name N
Campaign N
The other sheet, called "matrix", is generated by a VBA code the currently results in something like this:
Name 1
Name 2
Name 3
Name N
Campaign A
Campaign C
Campaign A
Campaign N
This variable-sized matrix can change the size of columns and rows based on the report I get. The actual workbook has much more content, I am just simplifying it with these examples. You can notice the empty cells because I don't know how to create the code to fill them. What I actually desire to be inside them is the number of campaigns each salesman is assigned to.
Desired Result:
Name 1
Name 2
Name 3
Name N
Campaign A
1
0
1
N
Campaign B
1
0
0
N
Campaign C
0
1
0
N
Campaign N
N
N
N
N
Basically what I need is to use the first row and column as parameters for a COUNTIFS to populate the matrix.
Can anyone help me with that? I'd really appreciate any tips coming my way! ;)
This is my first question in the community, I ask sorry in advance if I've done any mistakes. I feel ashamed to ask but I have no clue whatsoever on how to do this.
Function FnTwoDimentionDynamic()
Dim arrTwoD()
Dim intRows
Dim intCols
Dim i As Integer, j As Integer
intRows = Sheets("matrix").Cells(Rows.Count, 1).End(xlUp).Row - 1
intCols = Sheets("matrix").Cells(1, Columns.Count).End(xlToLeft).Column - 1
ReDim Preserve arrTwoD(1 To intRows, 1 To intCols)
'Here I am using a simple calculation just to see if will populate
'the variable range, but what I need is a COUNTIFS searching for
'the times a Salesman appears in certain Campaing
For i = 1 To intRows
For j = 1 To intCols
arrTwoD(i, j) = i * 2 + j ^ 2
Next j
Next i
Sheets("matrix").Select: Range("B2").Select
For i = 1 To intRows
For j = 1 To intCols
ActiveCell.Value = arrTwoD(i, j)
ActiveCell.Offset(0, 1).Select
Next j
ActiveCell.Offset(1, -intCols).Select
Next i
End Function
The following code makes a couple of assumptions, the first being that you are using Excel 365 and the data on the sheet deals list starts in A1.
If either of these are incorrect the code can be changed.
Also, I'm not sure how you are creating your 'matrix' so I've used code to do that at the start.
Option Explicit
Sub CreateMatrixAndCounts()
Dim wsDeals As Worksheet
Dim wsMatrix As Worksheet
Dim rngSalesmen As Range
Dim rngCampaigns As Range
Dim rngFormulas As Range
Dim arrUniqueSalesmen As Variant
Dim arrUniqueCampaigns As Variant
Set wsDeals = Sheets("Deals List")
With wsDeals
Set rngSalesmen = .Range("A2", .Range("A" & .Rows.Count).End(xlUp))
Set rngCampaigns = rngSalesmen.Offset(, 1)
arrUniqueSalesmen = Application.Sort(Application.Unique(rngSalesmen))
arrUniqueCampaigns = Application.Sort(Application.Unique(rngCampaigns))
End With
Set wsMatrix = Sheets.Add
wsMatrix.Range("A2").Resize(UBound(arrUniqueSalesmen)).Value = arrUniqueSalesmen
wsMatrix.Range("B1").Resize(, UBound(arrUniqueCampaigns)).Value = Application.Transpose(arrUniqueCampaigns)
Set rngFormulas = wsMatrix.Range("B2").Resize(UBound(arrUniqueSalesmen), UBound(arrUniqueCampaigns))
With rngSalesmen
rngFormulas.Formula = "=COUNTIFS(" & .Address(External:=True) & ", $A2, " & .Offset(, 1).Address(External:=True) & ", B$1)"
End With
End Sub

how to count duplicate names excel

I have a single column of data in Column A that looks like this:
Joe
Joe
Joe
John
John
Josh
Josh
Josh
Josh
Can someone please provide me with code that would sum the number of Joes, Johns, and Joshs and put the sum for each name in the adjacement column. Thank you in advance! Huge help.. I have 5000 rows of names
[Note]
The meaning of the question has been change. My answer refer to the original version of question.
You can use Dictionary class to get count of each name in a string. Please, see:
'needs reference to Sctipting Runtime dll
Sub DoSomething()
Dim s As String, result() As String
Dim i As Integer, counter As Integer
Dim dic As Dictionary, k As Variant
s = "Joe Joe Joe John John Josh Josh Josh Josh"
result = Split(s, " ")
Set dic = New Dictionary
With dic
.CompareMode = BinaryCompare
For i = LBound(result) To UBound(result)
If Not .Exists(result(i)) Then
.Add Key:=result(i), Item:=1
Else
k = dic(result(i))
dic(result(i)) = k + 1
End If
Next
End With
For Each k In dic.Keys
Debug.Print k, dic(k)
Next
Set dic = Nothing
End Sub
Result:
Joe 3
John 2
Josh 4
[EDIT]
As to the changed question, you have to change only one loop. instead of:
For i = LBound(result) To UBound(result)
'
Next
use:
'earlier (variable declaration section):
Dim wsh As Worksheet
'later:
Set wsh = Thisworkbook.Worksheets("SheetName")
i = 2
Do While wsh.Range("A" & i) <>""
If Not .Exists(wsh.Range("A" & i)) Then
.Add Key:=wsh.Range("A" & i), Item:=1
Else
k = dic(wsh.Range("A" & i))
dic(wsh.Range("A" & i)) = k + 1
End If
i = i +1
Loop
Final note:
I'd suggest to move your focus on array formula, which enables you to make any calculation.
Steps to do (MS Excel 2010 and higher):
1) Copy column A into new Sheet
2) Remove duplicates (use Menu)
3) Select column B and insert the following formula:
=SUM(IF((Sheet1!$A$1:$A$1000=$A1), 1, 0))
4) Accept fomula by pressing CTRL + SHIFT + ENTER

Count if statement excluding strings and not equal to a number in excel

I have an excel set for which I need to count entries based on names. They're all in the same column and there is supposed to be 4 of each entry. I need a formula to count the number of cells with the same entry that do NOT start with either "Retail" or "Commercial" and only return the names in the cells for which there is NOT 4. For example, if my data looks thusly:
NAME
Retail - John
Retail - Sue
Kara
Kara
Joe
Joe
Joe
Joe
Commercial
Sarah
I want a formula that will search this column, and only return "Kara - 2" and "Sarah - 1". The "Retail" and "Commercial" are excluded from the start and since "Joe"=4 I'm not concerned with that. Is there some way I can have this search the column, have it return the first count to meet that criteria to C1, the next one to C2 and so on until I have a column of just the non-compliant entries? I'd love an output like below:
NAME COUNT
Kara 2
Sarah 1
Thanks for looking, I really appreciate any help and advice you can offer!
If your data is in column A the results table will be in columns B & C after running this macro:
Sub MAIN()
Dim A As Range, wf As WorksheetFunction
Dim s1 As String, s2 As String
Dim col As Collection
Set A = Intersect(Range("A:A"), ActiveSheet.UsedRange)
Set wf = Application.WorksheetFunction
Set col = MakeColl(A)
s1 = "Retail"
s2 = "Commercial"
K = 1
For i = 1 To col.Count
v = col.Item(i)
If InStr(v, s1) = 0 And InStr(v, s2) = 0 Then
n = wf.CountIf(A, v)
If n <> 4 Then
Cells(K, "B").Value = v
Cells(K, "C").Value = n
K = K + 1
End If
End If
Next i
End Sub
Public Function MakeColl(rng As Range) As Collection
Set MakeColl = New Collection
Dim r As Range
On Error Resume Next
For Each r In rng
v = r.Value
If v <> "" Then
MakeColl.Add v, CStr(v)
End If
Next r
MsgBox MakeColl.Count
End Function

Vba how to compare 2 column

I'm making an heuristic analyse and i have the fallowing problem : I want to find in column D numbers that match with column J and replace them by a "0". You can see what I'm trying to do on this image :
Problem : Column D have multiples values per cell and column J have one value per cell.
some part of the code:
Dim i,j As Integer
Dim temp As String
Dim x As Integer
Dim d As String
i = Application.CountA(Range("E:E")) + 10
'number of cell with values
j = Application.CountA(Range("J:J")) + 10
For j = 11 To j
temp = Range("J" & j).Value
For i = 11 To i
d = Range("D" & i).Value
*For x = LBound(vec) To UBound(vec)
If vec(x) = temp Then
vec(x) = 0
Range("D" & i).Value = vec(x)
End If
Next
Next
Next
*-> Here it is the problem, i cant figured out how to pass over the coma "," in column D,and store the data. I want to compare the temp with value on "d", but "d" can i have multiple numbers on the same cell, like " 3, 2, 1", and if there is any match like temp = 3, then d= "0,2,1".
English is not my native language so i hope you can understand what i want.
Thanks!
I think your almost there you just need to split up each cell and then search then recreate and replace the string in the cell. Please note I've not tested this.
Dim i,j As Integer
Dim temp As String
Dim x As Integer
Dim d As String
i = Application.CountA(Range("E:E")) + 10
'number of cell with values
j = Application.CountA(Range("J:J")) + 10
For j = 11 To j
temp = Range("J" & j).Value
For i = 11 To i
d = Range("D" & i).Value
Vec = split(d, ",") 'split the cell
d = "" 'clear the string
For x = LBound(vec) To UBound(vec)
If vec(x) = temp Then
vec(x) = 0
End If
d = d & vec(x) & "," 'recreate the string
Next
Range("D" & i).Value = left(d, len(d) - 1) 'save the string without the last ,
Next
Next
you can do this with the below formula - no need for VBA
Make a new column somewhere near column D.
In your new column, use this FIND and Substitute formula:
=IF(NOT(ISERROR(FIND(J:J,D:D))),SUBSTITUTE(D:D,J:J,"0"),D:D)
This formula looks for the value in column J within the cells in column D.
If a match is found, 0 is substituted for the found number. Otherwise, column D is returned.
If you want, you can hide column D to avoid confusion.
in VBA you have a string function called Split(vString, delimiter) that will split a string into tokens using the specified delimiter. See
MSDN Library: VB Split Function
examples:
Mr Spreadsheet John Walkenbach: Split Function examples
VB-Helper

VBA Finding the next column based on an input value

In a program that I'm trying to write now I take two columns of numbers and perform calculations on them. I don't know where these two columns are located until the user tells me (they input the column value in a cell in the workbook that my code is located in).
For example, if the user inputted "A" and "B" as the columns where all the information is in I can perform calculations based on those values. Likewise if they wanted to analyze another worksheet (or workbook) and the columns are in "F" and "G" they could input those. The problem is that I'm asking the user to input those two columns as well as four others (the last four are the result columns). I did this in hopes that I would be able to make this flexible, but now inflexibility is acceptable.
My question is, if I'm given a value of where some information will be (let's say "F") how can I figure out what the column will be after or before that inputted value. So if I'm only given "F" I'll be able to create a variable to hold the "G" column.
Below are examples of how the variables worked before I needed to do this new problem:
Dim first_Column As String
Dim second_Column As String
Dim third_Column As String
first_Column = Range("B2").Text
second_Column = Range("B3").Text
third_Column = Range("B4").Text
Here the cells B2 - B4 are where the user inputs the values. Generally I want to be able to not have the B3 and B4 anymore. I feel like the Offset(0,1) might be able to help somehow but so far I've been unable to implement it correctly.
Thank you,
Jesse Smothermon
Here are two functions that will help you dealing with columns > "Z". They convert the textual form of a column to a column index (as a Long value) and vice versa:
Function ColTextToInt(ByVal col As String) As Long
Dim c1 As String, c2 As String
col = UCase(col) 'Make sure we are dealing with "A", not with "a"
If Len(col) = 1 Then 'if "A" to "Z" is given, there is just one letter to decode
ColTextToInt = Asc(col) - Asc("A") + 1
ElseIf Len(col) = 2 Then
c1 = Left(col, 1) ' two letter columns: split to left and right letter
c2 = Right(col, 1)
' calculate the column indexes from both letters
ColTextToInt = (Asc(c1) - Asc("A") + 1) * 26 + (Asc(c2) - Asc("A") + 1)
Else
ColTextToInt = 0
End If
End Function
Function ColIntToText(ByVal col As Long) As String
Dim i1 As Long, i2 As Long
i1 = (col - 1) \ 26 ' col - 1 =i1*26+i2 : this calculates i1 and i2 from col
i2 = (col - 1) Mod 26
ColIntToText = Chr(Asc("A") + i2) ' if i1 is 0, this is the column from "A" to "Z"
If i1 > 0 Then 'in this case, i1 represents the first letter of the two-letter columns
ColIntToText = Chr(Asc("A") + i1 - 1) & ColIntToText ' add the first letter to the result
End If
End Function
Now your problem can be solved easily, for example
newColumn = ColIntToText(ColTextToInt(oldColumn)+1)
EDITED accordingly to the remark of mwolfe02:
Of course, if you are not interested in the column names, but just want to get a range object of a specific cell in a given row right beneath a column given by the user, this code is "overkill". In this case, a simple
Dim r as Range
Dim row as long, oldColumn as String
' ... init row and oldColumn here ...
Set r = mysheet.Range(oldColumn & row).Offset(0,1)
' now use r to manipulate the cell right to the original cell
will do it.
You were on the right track with Offset. Here is a test function that shows a couple different approaches to take with it:
Sub test()
Dim first_Column As String
Dim second_Column As String
Dim third_Column As String
Dim r As Range
first_Column = Range("B2").Text
second_Column = Range("B2").Offset(1, 0).Text
third_Column = Range("B2").Offset(2, 0).Text
Debug.Print first_Column, second_Column, third_Column
Set r = Range("B2")
first_Column = r.Text
Set r = r.Offset(1, 0)
second_Column = r.Text
Set r = r.Offset(1, 0)
third_Column = r.Text
Debug.Print first_Column, second_Column, third_Column
End Sub
UPDATE: After re-reading your question I realize you were trying to do offsets based on a user-entered column letter. #rskar's answer will shift the column letter, but it will be a lot easier to work with the column number in code. For example:
Sub test()
Dim first_Col As Integer, second_Col As Integer
first_Col = Cells(, Range("B2").Text).Column
second_Col = first_Col + 1
Cells.Columns(first_Col).Font.Bold = True
Cells.Columns(second_Col).Font.Italic = True
End Sub
There are a few syntactical problems with #rskar's answer. However, it was helpful in producing a function that grabs a column "letter", based on an input column "letter" and a desired offset to the right:
Public Function GetNextCol(TheCol As String, OffsetRight As Integer) As String
Dim TempCol1 As String
Dim TempCol2 As String
TempCol1 = Range(TheCol & "1").Address
TempCol2 = Range(TempCol1).Offset(0, OffsetRight).Address(0, 0, xlA1)
GetNextCol = Left(TempCol2, Len(TempCol2) - 1)
End Function
In light of the comments of others (and they all raised valid points), here is a much better solution to the problem, using Offset and Address:
Dim first_Column As String
Dim second_Column As String
Dim p As Integer
first_Column = Range("B2").Text
second_Column = _
Range(first_Column + ":" + first_Column).Offset(0, 1).Address(0, 0, xlA1)
p = InStr(second_Column, ":")
second_Column = Left(second_Column, p - 1)
The above should work for any valid column name, "Z" and "AA" etc. included.
Make use of the Asc() and Chr() functions in VBA, like so:
Dim first_Column As String
Dim second_Column As String
first_Column = Range("B2").Text
second_Column = Chr(Asc(first_Column) + 1)
The Asc(s) function returns the ASCII code (in integer, usually between 0 and 255) of the first character of a string "s".
The Chr(c) function returns a string containing the character which corresponds to the given code "c".
Upper case letters (A thru Z) are ASCII codes 65 thru 90. Just google ASCII for more detail.
NOTE: The above code will be fine so long as the first_Column is between "A" and "Y"; for columns "AA" etc., it will take a little more work, but Asc() and Chr() will still be the ticket to coding for that.

Resources