Excel VBA find a range of same values in a column - excel

I need to write a macro that will find the cell range based on a value. A column will have the same value in a row, I need to find out what is the first and last column that has the same value in a row.
So the macro needs to find that "Jill Cross" range is a4 to a9
So far I don't have much, got a way to find the first occurrence of a value
Function GetFirstCell(CellRef As Range)
Dim l As Long
l = Application.WorksheetFunction.Match(CellRef.Value, Range("A1:A10000"), 0)
GetFirstCell = l
End Function
Now I need to loop through the next rows somehow to return the last row of an occurrence

If you have your first cell in a sorted list, a countif function will give you the last cell easily.
Function GetFirstCell(CellRef As Range) as long
Dim l As Long
l = Application.WorksheetFunction.Match(CellRef.Value, Range("A1:A10000"), 0)
GetFirstCell = l
End Function
function GetLastCell(cellRef as range, lFirstCell as long)
Dim l As Long
l = Application.WorksheetFunction.countif(Range("A1:A10000"), CellRef.Value)
GetLastCell = lFirstCell+l-1
End Function

This will work though it has its limitations (for example if the names aren't sorted and the name you are looking for is separated across multiple places in your range...). Of course you need to replace the ranges with those you want to check and "bob" with whatever name you are looking for. Also, the column is static so you may want to alter that part. This is an array formula so you will need to press [ctrl]+[shift]+[enter] in the formula bar to execute it.
="A"&MIN(IF(ROW(A1:A6)*(A1:A6="bob")=0, 99999999, ROW(A1:A6)*(A1:A6="bob")))&":A"&MAX(SI(ROW(A1:A6)*(A1:A6="bob")=0, -99999999, ROW(A1:A6)*(A1:A6="bob")))

Related

Convert vlookup to more than 255 characters via Excel VBA

I am looking for reverse vlookup with more than 255 characters in Excel VBA.
This is the formula based one which I took from this website.
=INDEX(F2:F10,MATCH(TRUE,INDEX(D2:D10=A2,0),0))
I have try to convert it in VBA. Here below sample code
Sub test()
'concat
Range("i1") = WorksheetFunction.TextJoin(" ", True, Range("g1:h1"))
'lookup
Sal1 = Application.WorksheetFunction.Index(Sheets("sheet1").Range("a1:a2"), Application.WorksheetFunction.Match(True, Application.WorksheetFunction.Index(Sheets("sheet1").Range("i1:i1") = Range("i1").Value, 0), 0))
'=INDEX($W$3:$W$162,MATCH(TRUE,INDEX($W$3:$W$162=U3,0),0))
End Sub
It works well but it didn't when i change the range("i1:i1") to range("i1:i2")
I'm not sure what that worksheet formula does that =INDEX(F2:F11,MATCH(A2,D2:D11,FALSE)) doesn't do.
This part Index(Sheets("sheet1").Range("i1:i2") = Range("i1").Value, 0) is comparing a 2-d array to a single value, which should result in a Type Mismatch error. Whenever you reference a multi-cell range's Value property (Value is the default property in this context), you get a 2-d array even if the range is a single column or row.
You could fix that problem with Application.WorksheetFunction.Transpose(Range("D1:D10")) to turn it into a 1-d array, but I still don't think you can compare a 1-d array to a single value and have it return something that's suitable for passing into INDEX.
You could use VBA to create the array's of Trues and Falses, but if you're going to go to that trouble, you should just use VBA to do the whole thing and ditch the WorksheetFunction approach.
I couldn't get it to work when comparing a single cell to a single cell like you said it did.
Here's one way to reproduce the formula
Public Sub test()
Dim rFound As Range
'find A2 in D
Set rFound = Sheet1.Range("D1:D10").Find(Sheet1.Range("A2").Value, , xlValues, xlWhole)
If Not rFound Is Nothing Then
MsgBox rFound.Offset(0, 2).Value 'read column f - same position as d
End If
End Sub
If that simpler formula works and you want to use WorksheetFunction, it would look like this
Public Sub test2()
Dim wf As WorksheetFunction
Set wf = Application.WorksheetFunction
MsgBox wf.Index(Sheet1.Range("F2:F11"), wf.Match(Sheet1.Range("A2").Value, Sheet1.Range("D2:D11"), False))
End Sub
Function betterSearch(searchCell, A As Range, B As Range)
For Each cell In A
If cell.Value = searchCell Then
betterSearch = B.Cells(cell.Row, 1)
Exit For
End If
betterSearch = "Not found"
Next
End Function
i found this code from above link and it is useful for my current search.Below examples i try to get value..
Kindly consider Row 1 to 5 as empty for A and B column because my table always start from Row 6
Row
A Column
B Column
6
54
a
7
55
b
8
56
c
VBA Code:
Sub look_up ()
Ref = "b"
look_up = betterSearch(Ref, Range("B6:B8"), Range("A6:A8"))
End Sub
it show Empty while use Range("B6:B8"), Range("A6:A8")
but when changing the range from B6 and A6 to B1 and A1 (Range("B1:B8"), Range("A1:A8") )it gives the value...
My question is "can get the values from desired range"
Expressing matches via VBA
I like to know if there (are) any possibilities to convert this formula.
=INDEX(F2:F10,MATCH(TRUE,INDEX(D2:D10=A2,0),0))
So "reverse VLookUp" in title simply meant to express the (single) formula result via VBA (btw I sticked to the cell references in OP, as you mention different range addresses in comments).
This can be done by simple evaluation to give you a starting idea:
'0) define formula string
Dim BaseFormula As String
BaseFormula = "=INDEX($F$2:$F$10,MATCH(TRUE,INDEX($D$2:$D$10=$A2,0),0))"
'1) display single result in VB Editor's immediate
Dim result
result = Evaluate(BaseFormula)
Debug.Print IIf(IsError(result), "Not found!", result)
On the other hand it seems that you have the intention to extend the search string range
from A2 to more inputs (e.g. till cell A4). The base formula wouldn't return a results array with this formula,
but you could procede as follows by copying the start formula over e.g. 3 rows (note the relative address ...=$A2 to allow a row incremention in the next rows):
'0) define formula string
Dim BaseFormula As String
BaseFormula = "=INDEX($F$2:$F$10,MATCH(TRUE,INDEX($D$2:$D$10=$A1,0),0))"
'2) write result(s) to any (starting) target cell
'a)Enter formulae extending search cells over e.g. 3 rows (i.e. from $A2 to $A4)
Sheet3.Range("H2").Resize(3).Formula2 = BaseFormula
'b) optional overwriting all formulae, if you prefer values instead
'Sheet3.Range("H2").Resize(3).Value = Tabelle3.Range("G14").Resize(3).Value
Of course you can modify the formula string by any dynamic replacements (e.g. via property .Address(True,True,External:=True) applied to some predefined ranges to obtain absolute fully qualified references in this example).
Some explanations to the used formulae
The formula in the cited link
=INDEX(F2:F10,MATCH(TRUE,INDEX(D2:D10=A2,0),0))
describes a way to avoid an inevitable #NA error when matching strings with more than 255 characters directly.
Basically it is "looking up A2 in D2:D10 and returning a result from F2:F10" similar to the (failing) direct approach in such cases:
=INDEX(F2:F11,MATCH(A2,D2:D11,FALSE))
The trick is to offer a set of True|False elements (INDEX(D2:D10=A2,0))
which can be matched eventually without problems for an occurence of True.
Full power by Excel/MS 365
If, however you dispose of Excel/MS 365 you might even use the following much simpler function instead
and profit from the dynamic display of results in a so called spill range.
That means that matches can be based not only on one search string, but on several ones (e.g. A1:A2),
what seems to solve your additional issue (c.f. last sentence in OP) to extend the the search range as well.
=XLOOKUP(A1:A2,D2:D10,F2:F10,"Not found")

Create list of unique values from a range in Excel

I have data (names) spread out in a sheet in Excel. It is in columns A through M.
Some rows have data in every column (A-M), some rows have nothing.
I want to extract a list of every unique value (or even a list of every value, and then I can remove duplicates) in the range.
For the life of me, I cannot figure out how to do this and haven't found an example online that pulls unique values from a RANGE, and not a single column.
Any ideas? Let me know if you need a screenshot of the data for more context.
Give this macro a try:
Sub MAINevent()
Dim it As Range, r As Range, x0
With CreateObject("scripting.dictionary")
For Each it In Range("A:M").SpecialCells(2)
x0 = .Item(it.Value)
Next
Set r = Cells(1, "N").Resize(.Count, 1)
r.Value = Application.Transpose(.Keys)
End With
End Sub
You can use Go to Special (Ctrl+G -> Special) to select blanks and remove them (Ctrl+- with cells selected). Then use the formula described here to stack all your columns in one, on top of each other. Then replace formulas with values and finally, use Remove Duplicates.

Excel: Replace text based on a substring of the text

Column D contains the substring to look for, which could be anywhere in the search field of column A. Column E contains the value to replace the content of column A with. See the results in Column B. Can anyone help me out?
After implementing suggested solution
If you are fine with it, I suggest a modification to the arrangement.
Arrange the lookup values as in the image, in columns F, G and H and the replace values in the next corresponding row. You would need 3 helper columns, (since you have 3 lookup values). Column B, C and D are helper columns.
Enter the below formula in column B, B1,
=IFERROR(IF(MATCH("*"&F$1&"*",$A1,0),F$2),"")
Just drag it to the bottom throughout the range, and then drag it to right upto column E.
Column F is your actual output. Enter the formula in column F,
=B1&C1&D1
and drag it down. Hope this helps. Let me know if you need anything else.
This works, but is not very elegant and won't easily scale.
=CONCATENATE(REPT($E$1,COUNTIF(A1,"*"&$D$1&"*")),REPT($E$2,COUNTIF(A1,"*"&$D$2&"*")),REPT($E$3,COUNTIF(A1,"*"&$D$3&"*")))
It's series of REPT(Replacement,COUNTIF(Values,*Labels*)). The *'s around the search allow it to be wildcard, essentially making the COUNTIF's flags of if the element is included or not. We then repeat the replacement value the number of times the countif is present (which in the example will always be 1), and then concatenate the results.
I have created a VB script to solve this:
Sub DetermineFeatures()
Dim featureLabel As String
Dim testString As String
testString = "FEAT_EMV_L4, Team_1, Team_IBM"
Dim stringValue As Variant
Dim lookupRange As Range
Set lookupRange = Worksheets("LookupTable").Range("A1:B100")
Dim outputWorksheet As Worksheet
Set outputWorksheet = Worksheets("Output")
Dim feature As String
Dim rowIndex As Long
For Each cell In Worksheets("Original").Columns("O").Cells
rowIndex = cell.Row
cellValue = WorksheetFunction.Trim(cell.Value)
For Each stringValue In Split(cellValue, ",")
If InStr(stringValue, "LABEL_") Then
featureLabel = Trim(stringValue)
' Call the Vlookup function to look up the actual feature name
for the supplied label. This
' returns the value from the second column of the range within
the LookupTable worksheet.
feature = Application.WorksheetFunction.VLookup(featureLabel,
lookupRange, 2, False)
outputWorksheet.Cells(rowIndex, 1) = feature
If Not IsEmpty(featureLabel) Then Exit For
End If
Next
Next cell
End Sub

How to get every value from one column to another column based on its corresponding value

As you can see in the image that "Col B" has same number multiple times. For ex: "1" is four times, "2" is three times, and so on. However, all these numbers correspond to a specific number from "Col A". What I am trying to do is get the the column I have highlighted in orange and yellow. You can clearly see what I have done. What I need is a excel function that does it for me. This is just a sample. I have dataset with million data points, and I can't type all that.
Thanks!!
Formula for cell E2
=IFERROR(SMALL(IF($B:$B=$D2,ROW($B:$B)-1),COLUMN(A:A)),"")
Entered as an Array Formula (Enter with Ctrl-Shift-Enter rather than just Enter)
Copy accross for as many cells as you wish
Note: this formula is quite slow. A well designed UDF will be faster.
One way to solve this with a UDF is
Function MultiLookup(Val As Variant, rItems As Range, rLookup As Range, Index As Long) As Variant
Dim vItems As Variant
Dim i As Long, n As Long
With rItems
If IsEmpty(.Cells(.Count)) Then
Set rItems = Range(.Cells(1, 1), .Cells(.Count).End(xlUp))
End If
End With
vItems = rItems
n = 0
For i = 1 To UBound(vItems, 1)
If vItems(i, 1) = Val Then
n = n + 1
If n = Index Then
MultiLookup = rLookup.Cells(i, 1)
Exit Function
End If
End If
Next
MultiLookup = vbNullString
End Function
Use like this, for cell E2
=MultiLookup($D2,$B:$B,$A:$A,COLUMN(A:A))
Again, copy accross for as many cells as use wish
Here is another non UDF solution, that works fast. Please note that it uses the AGGREGATE-Function, which is only available since Excel-2010.
Put this in E2 and drag across.
=INDEX($A$2:$A$1000,AGGREGATE(15,6,Row($1:$1000)/($B$2:$B$1000=$D2),COLUMN(A:A)))
You can also wrap this formula with a IFERROR to make sure you don't get the #VALUE! Error.
A PivotTable, with it's body copied into D2 and then blanks removed should be quick:

How to count the number of occurrences of a string given it also matches another cell

So I have the following:
=SUM(LEN(Data!$J$2:$J$9999)-LEN(SUBSTITUTE(Data!$J$2:$J$9999,"dependent","")))/LEN("dependent")
Which given the range j2:j9999 will count the number of occurrences of the string dependent, regardless of the rest of the cells - ie if the cell contains primary,dependent and another is dependent the count would be 2.
What I need and cant really figure out is given the above how would i also reference another cell in the row. So if i still need to count the occurrences of the the string dependent, how would i limit that to just count the occurrences if both the string dependent was found and also another cell in the same row as the cell with dependent == foo.
EXAMPLE:
column1, column2, column3
foo, bar, dependent
bar, baz, dependent
foo, bar, primary dependent dependent1
In this example i would like to do a pseudo countifs where i count the occurrences of the string dependent given the row also matches foo.
The count here would be 3 because row 1 has foo in cell1 and has dependent and row 3 has foo in column1 and 2 occurrences of dependent in column 3
A version that doesn't require array-entry:
=SUMPRODUCT((Data!$H$2:$H$9999="foo")*(LEN(Data!$J$2:$J$9999)-LEN(SUBSTITUTE(Data!$J$2:$J$9999,"dependent",""))))/LEN("dependent")
or, if the "dependent" will only occur once per cell, you could just use:
=COUNTIFS(Data!$H$2:$H$9999,"foo",Data!$J$2:$J$9999,"*dependent*")
This should hopefully work. It's done based off your sample data, and returns the value in D1:
Sub StringCounter_II_The_Sequel()
Dim range1 As Range, rCell As Range
Dim string1 As String
Dim length As Long, i As Long
string1 = "dependent"
length = Len(string1)
Set range1 = ActiveSheet.Range("C:C")
For Each rCell In range1
txt = rCell.Text
If InStr(txt, string1) > 0 And rCell.Offset(,-2) = "foo" Then
i = i + (Len(txt) - Len(Replace(txt, string1, ""))) / length
End If
Next rCell
ActiveSheet.Range("D1").Value = i
End Sub
You can add an IF to your formula, like this:
=SUM(IF(Data!H2:H9999="foo",LEN(Data!$J$2:$J$9999)-LEN(SUBSTITUTE(Data!$J$2:$J$9999,"dependent","")))/LEN("dependent"),0)
Obviously, it's still an array formula, entered with ctrl-shift-enter

Resources