Excel Index Match - List All Results - excel

I am using the following Index Match function to get the name of a company where the spend data matches that of which I type into cell BF17.
=INDEX($AM$16:$BB$16,MATCH(BF17,AM17:BB17,0))
What I want to be able to do is list multiple results within the same cell and separate these with a comma.
Does anyone know if this is possible and if so can someone please show me how?
Thanks

Code:
Insert this code in a module in your workbook:
Public Function hLookupList(KeyVal, Vals As Range, Ret As Range) As String
Dim i As Long
Dim vw As Worksheet
Dim rw As Worksheet
Dim RetStr As String
Application.Volatile True
Set vw = Vals.Worksheet
Set rw = Ret.Worksheet
If Vals.Rows.Count > 1 Then
hLookupList = "Too Many Value Rows Selected!"
Exit Function
End If
If Ret.Rows.Count > 1 Then
hLookupList = "Too Many Return Rows Selected!"
Exit Function
End If
If Vals.Columns.Count <> Ret.Columns.Count Then
hLookupList = "Value Range and Return Range must be the same size!"
Exit Function
End If
For i = Vals.Column To Vals.Column + Vals.Columns.Count - 1
If vw.Cells(Vals.Row, i) = KeyVal Then
RetStr = RetStr & rw.Cells(Ret.Row, Ret.Column + i - 1) & ", "
End If
Next i
hLookupList = Left(RetStr, Len(RetStr) - 2)
End Function
Then:
Insert this in the cell where you want your list: =hLookupList(BF17, $AM$16:$BB$16, $AM$17:$BB$17)

Unfortunately there is no built-in way to make a vlookup or index/match function return an array. You could do it with a custom formula or if you know there are a limited number of results, a few nested lookups. Lewiy at mrexcel.com wrote a great custom function that I use, which can be found here. This function can be slow if you are looking up a large number of rows.
Since you are looking up columns and want commas separating the results instead of spaces, you will need to modify the code as follows:
Function MYVLOOKUP(lookupval, lookuprange As Range, indexcol As Long)
Dim r As Range
Dim result As String
result = ""
For Each r In lookuprange
If r = lookupval Then
result = result & "," & r.offSet(indexcol, 0)
End If
Next r
result = Right(result, Len(result) - 1)
MYVLOOKUP = result
End Function
Your formula would then be =MYVLOOKUP(BF17,AM17:BB17,-1)
If you want a space after the comma (in the results), change:
result = result & "," & r.offSet(indexcol, 0)
to
result = result & ", " & r.offSet(indexcol, 0)
If you haven't used custom functions before, hit Alt + F11 when in Excel to bring up the VBE, and add a new module to the workbook you are working on (Insert --> Module). Just copy and paste this code in there. I would recommend Paste Special --> Values before sending the workbook to anyone. Let me know if you have any questions implementing it!

Related

Pass Multiple Values and Get Selected Results In A Cell

I am new with Excel VBA and trying to use it for a scenario in an Excel work book. I am trying to do a multiple value search in a cell and that should be highlighted. Say I've these ids - 1001, 1002, so in the specific cell these values should be highlighted or searched. I am not sure how can I use List<> in VBA but in C#, I can do the following:
List<string> aLst = new List<string>();
aLst.Add("1001");
aLst.Add("1002");
So with the list, I can iterate the ids and get the results matched. I was looking into the following VBA code and it gets one value as parameter. Then checks the matched one:
Function SingleCellExtract(Lookupvalue As String, LookupRange As Range, ColumnNumber As Integer)
Dim i As Long
Dim Result As String
For i = 1 To LookupRange.Columns(1).Cells.Count
If LookupRange.Cells(i, 1) = Lookupvalue Then
Result = Result & " " & LookupRange.Cells(i, ColumnNumber) & ","
End If
Next i
SingleCellExtract = Left(Result, Len(Result) – 1)
End Function
Here is the link that I am following: Excel VBA
So any way that I can use List<> and search the required values highlighted in the excel sheet?
Sample:
Id - Name
1001 John
1002 Jack
So copy this function into a new module.
Then you can access either via another function or procedure or through an excel formula in a cell like =MultiCellExtract(A2:A3;A2:B3;2)
' LookupValuesRange is an Excel Range of cells
Public Function MultiCellExtract(LookupValuesRange As Range, LookupRange As Range, ColumnNumber As Integer) As String
Dim cellValue As Range
Dim i As Long
Dim Result As String
For Each cellValue In LookupValuesRange
For i = 1 To LookupRange.Columns(1).Cells.Count
If LookupRange.Cells(i, 1) = cellValue.Value Then
Result = Result & " " & LookupRange.Cells(i, ColumnNumber) & ","
End If
Next i
Next cellValue
MultiCellExtract = Left(Result, Len(Result) - 1)
End Function
Let me know if it helps or we can adjust it.

How to return name list using excel formula?

Data is:
-|-A--|-B-|
1|BAT | 5 |
2|CAT | 2 |
3|RAT | 4 |
I want formula that gives name list of animal if value in columns B > 3.
From example Result is BAT,RAT.
Here is one of the approach using you can get what you looking for. BUT you need to use VBA help to achieve this by creating user defined function.
To implement user defined function in excel:
1. Press Alt-F11 to open visual basic editor
2. Click Module on the Insert menu
3. Copy and paste below metioned user defined function
4. Exit visual basic editor
Here is the function:
Function Lookup_concat(Search_in_col As Range, Return_val_col As Range)
Dim i As Long
Dim result As String
For i = 1 To Search_in_col.Count
If Search_in_col.Cells(i, 1) > 3 Then
If result = "" Then
result = Return_val_col.Cells(i, 1).Value
Else
result = result & ", " & Return_val_col.Cells(i, 1).Value
End If
End If
Next
Lookup_concat = Trim(result)
End Function
And now in cell C1, paste the formula:
=Lookup_concat(B2:B4,A2:A4)
And this will return BAT, RAT.
Explaination of user defined function:
Lookup_concat(Search_in_column, Concatenate_values_in_column)
Looks for a value which is greater than 3 in a column and then returns values in the same rows from a column you specify, concatenated into a single cell.
Following the VB approach as smartly suggested by #Nelly27281, I propose the following function with works with variables for the criteria, range, input and output columns making it more flexible.
'Variables Type Description Sample
'sCriteria String The criteria to search for “>3”
'rInput Range The whole range to work with A1:B4
'bColSearch Byte Column within the rInput range to search for using “sCriteria” 2
'bColOutput Byte Column within the rInput range to obtain the output value 1
'blHasHdr Boolean (Optional) True if the “rinput” range has header, default is false 1
Public Function rSearch_sOutput(sCriteria As String, _
rInput As Range, bColSearch As Byte, bColOutput As Byte, Optional blHasHdr As Boolean)
Dim sOutput As String
Dim L As Long, L0 As Long
Rem Set Output String
L0 = IIf(blHasHdr, 2, 1)
With rInput
For L = L0 To .Rows.Count
If Application.Evaluate(.Columns(bColSearch).Cells(L).Value2 & sCriteria) Then
If sOutput = Empty Then
sOutput = .Columns(bColOutput).Cells(L).Value2
Else
sOutput = sOutput & ", " & .Columns(bColOutput).Cells(L).Value2
End If: End If: Next: End With
Rem Set Results
rSearch_sOutput = sOutput
End Function
To use it enter the following formula:
=rSearch_sOutput(">3",A1:B4,2,1,1)

How to check if the previous and next records are sequential in Excel

I have an excel Spreadsheet of values. I am trying to build a string of values which will look at all the records in the sheet and determine which ones are the same (based on a sequence)..
As you can see by the picture, there are three columns (E, F, G) which contain the source data. (source ID, target ID and Connection ID).. essentially there can only be one combination of source to target relationships, so I will need to merge any duplicate connections.
so far I have managed to find when they are duplicates by:
concatenating the source and target (Col H)
looking for duplicates (and ordering them) using the formula
=IF(COUNTIF(H:H,H2)>1,COUNTIF(H$2:H2,H2),1)
and Now I am trying to build a string which will be used to merge the records.
Essentially I am trying to build a function which looks for all exact strings in Col H, and then looks at the sequence(I) and builds a string like so:
34~62~65 (which tells me that connection 34 must merge with 62 and then 65)
Problem is that I have not managed to do this.
current formula in Col J is:
=IF(H2=H3,IF(I3=I2+1,G3&"~"&G2,""))
but as you can see its only pairwise, not actually looking for the duplicates in sequence (i.e. 1 then 2 then 3 etc)
A while ago I wrote a quite an extensive UDF for a friend of mine to deal with this problem. It is supposed to look exactly like a VLookup, except for an additional parameter UniqueOnly and a Separator.
What it does is it looks up a value based on a different cell just like VLookup, but unlike Vlookup it returns all possible values as a result, not just one.
It is used like this:
=LookupConcatenate(LookupValue,LookupRange,LookupColumn, [Optional UniqueOnly = 0], [Optional Separator = ", "])
And the code is:
Public Function LookupConcatenate(LookupValue As Range, LookupRange As Range, Column As Integer, Optional UniqueOnly As Boolean = False, Optional Separator As String = ", ") As String
' by Marek Stejskal
Dim rngMatch As Range
Dim rngLookup As Range
Dim varMatch As Variant
Dim varIndex As Variant
Dim intFoundAll As Integer
Dim strFoundAll() As String
Dim intFoundUnique As Integer
Dim strFoundUnique() As String
Dim blnFound As Boolean
Dim strResult As String
Dim i As Integer
On Error GoTo ErrHandler:
Set rngLookup = LookupRange
Set rngMatch = rngLookup.Columns(1)
Do While 1 = 1
' Match function
varMatch = Application.Match(LookupValue, rngMatch, 0)
' Exit checking if MATCH returned no value
If IsError(varMatch) Then Exit Do
' Index function
varIndex = Application.Index(rngLookup, varMatch, Column)
intFoundAll = intFoundAll + 1
' Adding space to ALL array
ReDim Preserve strFoundAll(1 To intFoundAll)
' Checking if the new result is in ALL array
blnFound = False
For i = 1 To UBound(strFoundAll)
If strFoundAll(i) = CStr(varIndex) Then
blnFound = True
Exit For
End If
Next
' If new result is unique add it to UNIQUE array
If blnFound = False Then
intFoundUnique = intFoundUnique + 1
ReDim Preserve strFoundUnique(1 To intFoundUnique)
strFoundUnique(intFoundUnique) = CStr(varIndex)
End If
' Add the new result to ALL array
strFoundAll(intFoundAll) = CStr(varIndex)
' Shortening ranges
Set rngLookup = rngLookup.Resize(rngLookup.Rows.Count - varMatch).Offset(varMatch)
Set rngMatch = rngLookup.Columns(1)
Loop
' Creating result string
If UniqueOnly = True Then
If intFoundUnique = 0 Then
strResult = ""
Else
For i = 1 To UBound(strFoundUnique)
strResult = strResult & IIf(strResult = "", "", Separator) & strFoundUnique(i)
Next i
End If
Else
If intFoundAll = 0 Then
strResult = ""
Else
For i = 1 To UBound(strFoundAll)
strResult = strResult & IIf(strResult = "", "", Separator) & strFoundAll(i)
Next i
End If
End If
LookupConcatenate = strResult
Exit Function
ErrHandler:
LookupConcatenate = Err.Description
End Function
To make this work for you, you will first need to switch the order of Connection and ID and then you can put on row 2 the formula like this:
=LookupConcatenate(G2, G2:J100, 2, 0, "~")
So if you want to do this without VBA, the only way is to build the string as you go down each row. What I mean is the final data would look like:
This does not meet the full requirements of all of column "F" containing the full concatenated string. But the last unique row of ID would contain the final string.
The formula to put in column F (assuming your data is aligned as in the picture here)
=IF(ISERROR(MATCH($D2,INDIRECT("D1:D"&ROW()-1),0)),""&$C2,IFERROR(INDEX(F:F,MATCH($D2,INDIRECT("D1:D"&ROW()-1),1)),INDEX(F:F,MATCH($D2,INDIRECT("D1:D"&ROW()-1),0)))&"~"&$C2)
This works even if the rows are not sorted, (and it actually does not use the sequence column at all). Here is a picture with additional rows added as test data:
You actually then could create the column you are searching for, by adding a column containing:
=IF(COUNTIF($F:$F,SUBSTITUTE($F2,"~","*")&"*")=1,$F2,FALSE)
That would give the following final result:

Excel VBA: Unlimited Range Inputs With Versatility (?)

Could someone help me create a function that will handle an unlimited number of diverse ranges? I have tried "Paramarray variables() as Variant" and "variable as Range" in my arguments list but neither of them provide the versatility that I am looking for.
The key is that I want my function to be able to simultaneously handle things like "MyFunc(A1:A10, B1)" or "MyFunc(A1, B1:10, C11)". The problem I'm finding is that "ParamArray" can only handle comma separated inputs while "variable as Range" can only handle non-comma separated inputs.
Basically, I want to have the same functionality that the SUM() function has. Where SUM can handle an infinite (sort of) number of inputs regardless if they are separated by commas or are in a range.
As requested, here is my code:
Function COMMA_DELIMITER(inputs as Range)
' this function basically concatenates a consecutive set of cells and places commas between values
For Each j in Inputs
stringy = stringy & j.value & chr(44)
Next
stringy = Left(stringy, Len(stringy) - 1)
COMMA_DELIMITER = stringy
End Function
or
Function COMMA_DELIMITER_A(ParamArray others())
'this is the same function, only the cells don't have to be consecutive
For i = 1 to UBound(others) + 1
stringy = stringy & others(i-1) & chr(44)
Next
COMMA_DELIMIERTER_A = Left(stringy, Len(stringy) - 1)
End Function
I pretty much want to create a function that has the flexibility to handle both consecutive cells and/or non-consecutive cells. The inputs would look like this, "=MyFunc(A1, B1:B10, C11, D12:D44)".
Could someone help me create a function that can handle something like this, "MyFunc(A1, B1:B10, C11, D12:D44)"?
Thanks,
Elias
Actually it is possible to do that, and code from chris neilsen is almost there.
Function MyFunc1(ParamArray r()) As Variant
Dim rng As Range
Dim i As Long
Dim j As Variant
Dim s As String
For i = LBound(r) To UBound(r)
For each j in r(i)
s = s & " " & j.Address
Next
Next
MyFunc1 = s
End Function
See? You only have to put one more loop, so if you have a range like [A1:A4] it will loop for into that too. One loop will return another ParamArray, so you have to loop for twice. And if you have just [A1] that's not a problem either, the double looping will cause no problem.
I think there are two issues with your approach
, and   (comma and space) are the Union and Intersect operators for ranges
To pass a multi area range into a single parameter, you need to enclose it in ( )
To demonstrate
ParamArray version
Loop through the array variable to access to individual ranges
Function MyFunc1(ParamArray r()) As Variant
Dim rng As Range
Dim i As Long
Dim s As String
For i = LBound(r) To UBound(r)
s = s & " " & r(i).Address
Next
MyFunc1 = s
End Function
 
Range version
Iterate the range Areas collection to access individual ranges
Function MyFunc2(r As Range) As Variant
Dim rng As Range
Dim i As Long
Dim s As String
For Each rng In r.Areas
s = s & " " & rng.Address
Next
MyFunc2 = s
End Function
Both
=MyFunc1(B5:D5 C4:C6,B10:E10,D13:D16)
and
=MyFunc2((B5:D5 C4:C6,B10:E10,D13:D16))
will return
$C$5 $B$10:$E$10 $D$13:$D$16
(note that the intersection of B5:D5 and C4:C6 is C5)

excel vlookup with multiple results

I am trying to use a vlookup or similar function to search a worksheet, match account numbers, then return a specified value. My problem is there are duplicate account numbers and I would like the result to concatenate the results into one string.
Acct No CropType
------- ---------
0001 Grain
0001 OilSeed
0001 Hay
0002 Grain
Is in the first worksheet, on the 2nd worksheet I have the Acct No with other information and I need to get all the matching results into one column on the 2nd worksheet ie. "Grain Oilseed Hay"
Here is a function that will do it for you. It's a little different from Vlookup in that you will only give it the search column, not the whole range, then as the third parameter you will tell it how many columns to go left (negative numbers) or right (positive) in order to get your return value.
I also added the option to use a seperator, in your case you will use " ". Here is the function call for you, assuming the first row with Acct No. is A and the results is row B:
=vlookupall("0001", A:A, 1, " ")
Here is the function:
Function VLookupAll(ByVal lookup_value As String, _
ByVal lookup_column As range, _
ByVal return_value_column As Long, _
Optional seperator As String = ", ") As String
Dim i As Long
Dim result As String
For i = 1 To lookup_column.Rows.count
If Len(lookup_column(i, 1).text) <> 0 Then
If lookup_column(i, 1).text = lookup_value Then
result = result & (lookup_column(i).offset(0, return_value_column).text & seperator)
End If
End If
Next
If Len(result) <> 0 Then
result = Left(result, Len(result) - Len(seperator))
End If
VLookupAll = result
End Function
Notes:
I made ", " the default seperator for results if you don't enter one.
If there is one or more hits, I added some checking at the end to
make sure the string doesn't end with an extra seperator.
I've used A:A as the range since I don't know your range, but
obviously it's faster if you enter the actual range.
One way to do this would be to use an array formula to populate all of the matches into a hidden column and then concatenate those values into your string for display:
=IFERROR(INDEX(cropTypeValues,SMALL(IF(accLookup=accNumValues,ROW(accNumValues)-MIN(ROW(accNumValues))+1,""),ROW(A1))),"")
cropTypeValues: Named range holding the list of your crop types.
accLookup: Named range holding the account number to lookup.
accNumValues: Named range holding the list of your account
numbers.
Enter as an array formula (Ctrl+Shift+Enter) and then copy down as far as necessary.
Let me know if you need any part of the formula explaining.
I've just had a similar problem and I have looked up similar solutions for a long time, nothing really convinced me though. Either you had to write a macro, or some special function, while yet, for my needs the easiest solution is to use a pivot table in e.g. Excel.
If you create a new pivot table from your data and first add "Acct No" as row label and then add "CropType" as RowLabel you will have a very nice grouping that lists for each account all the crop types. It won't do that in a single cell though.
Here is my code which even better than an excel vlookup because you can choose to criterie colum, and for sure a separator (Carriege return too)...
Function Lookup_concat(source As String, tableau As Range, separator As String, colSRC As Integer, colDST As Integer) As String
Dim i, y As Integer
Dim result As String
If separator = "CRLF" Then
separator = Chr(10)
End If
y = tableau.Rows.Count
result = ""
For i = 1 To y
If (tableau.Cells(i, colSRC) = source) Then
If result = "" Then
result = tableau.Cells(i, colDST)
Else
result = result & separator & tableau.Cells(i, colDST)
End If
End If
Next
Lookup_concat = result
End Function
And a gift, you can make also a lookup on multiple element of the same cell (based on the same separator). Really usefull
Function Concat_Lookup(source As String, tableau As Range, separator As String, colSRC As Integer, colDST As Integer) As String
Dim i, y As Integer
Dim result As String
Dim Splitted As Variant
If separator = "CRLF" Then
separator = Chr(10)
End If
Splitted = split(source, separator)
y = tableau.Rows.Count
result = ""
For i = 1 To y
For Each word In Splitted
If (tableau.Cells(i, colSRC) = word) Then
If result = "" Then
result = tableau.Cells(i, colDST)
Else
Dim Splitted1 As Variant
Splitted1 = split(result, separator)
If IsInArray(tableau.Cells(i, colDST), Splitted1) = False Then
result = result & separator & tableau.Cells(i, colDST)
End If
End If
End If
Next
Next
Concat_Lookup = result
End Function
Previous sub needs this function
Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
IsInArray = (UBound(Filter(arr, stringToBeFound)) > -1)
End Function
Function VLookupAll(vValue, rngAll As Range, iCol As Integer, Optional sSep As String = ", ")
Dim rCell As Range
Dim rng As Range
On Error GoTo ErrHandler
Set rng = Intersect(rngAll, rngAll.Columns(1))
For Each rCell In rng
If rCell.Value = vValue Then
VLookupAll = VLookupAll & sSep & rCell.Offset(0, iCol - 1).Value
End If
Next rCell
If VLookupAll = "" Then
VLookupAll = CVErr(xlErrNA)
Else
VLookupAll = Right(VLookupAll, Len(VLookupAll) - Len(sSep))
End If
ErrHandler:
If Err.Number <> 0 Then VLookupAll = CVErr(xlErrValue)
End Function
Use like this:
=VLookupAll(K1, A1:C25, 3)
to look up all occurrences of the value of K1 in the range A1:A25 and to return the corresponding values from column C, separated by commas.
If you want to sum values, you can use SUMIF, for example
=SUMIF(A1:A25, K1, C1:C25)
to sum the values in C1:C25 where the corresponding values in column A equal the value of K1.
ALL D BEST.

Resources