I have a column that has a list of genomes which are special. There is another column of genomes in which some cells have multiple entries, comma-separated. What I want is to check if for each cell of the second column, is any of its entries in the first column. If the cells in second column only had one element, I could do it with =NOT(ISERROR(MATCH(B2,$A$2:$A$120,0))) but since some query cells have muliple elements, this will not work for those. Any suggestions?
Try SUMPRODUCT and SEARCH:
=IF(SUMPRODUCT(--ISNUMBER(SEARCH(","&$A$2:$A$5&",",","&B2&","))),"Y","N")
Explanation:
I will explain logic for cell C2 in my example.
In first step formula got values from ranges and results into:
=IF(SUMPRODUCT(--ISNUMBER(SEARCH({",AA,",",CC,",",EE,",",Gg,"},",AA,BB,"))),"Y","N")
SEARCH function returns array of positions of found strings or error if not found:
=IF(SUMPRODUCT(--ISNUMBER({1,#VALUE,#VALUE,#VALUE})),"Y","N")
ISNUMBER returns array of booleans:
=IF(SUMPRODUCT(--{TRUE,FALSE,FALSE,FALSE}),"Y","N")
-- converts booleans to integers:
=IF(SUMPRODUCT({1,0,0,0}),"Y","N")
SUMPRODUCT sums it up:
=IF(1,"Y","N")
IF returns TRUE part if first argument is nonzero value.
If you need to keep the rows in the genomes column, then Separate genomes column in 3 columns and do the MATCH in 3 new columns B,C & D.
=NOT(ISERROR(MATCH(A2,$B$2:$D$120,0)))
Edit: or you can try to use a VB macro and a regular expression:
Public Function RegexExecute(str As String, reg As String, _
Optional matchIndex As Long, _
Optional subMatchIndex As Long) As String
On Error GoTo ErrHandl
Set regex = CreateObject("VBScript.RegExp"): regex.Pattern = reg
regex.Global = Not (matchIndex = 0 And subMatchIndex = 0) 'For efficiency
If regex.test(str) Then
Set matches = regex.Execute(str)
RegexExecute = matches(matchIndex).SubMatches(subMatchIndex)
Exit Function
End If
ErrHandl:
RegexExecute = CVErr(xlErrValue)
End Function
reference
Related
I have a spreadsheet where users enter their three character name code corresponding to everyday with number of hours that they plan to spend in a specific task (A, B, C) within brackets. What I want is these needs to be summed up for every user and get populated in rows corresponding to their names everyday.
Currently I am using an custom vba function to perform this sum, however I wanted to know if this can be done directly with any custom formula or VLOOKUP. Any help is appreciated!
Edit: I have been using the function, one specific to each user (the three character code) to calculate this sum.
Function SumJON(Target As Range) As Double
Dim xCell As Range
Dim xSum As Double
xSum = 0
For Each xCell In Target
If xCell.Value <> "" And InStr(xCell.Value, "JON") > 0 Then
Test = Split(xCell.Value, "JON")
Test1 = Test(1)
Test2 = Split(Test1, ")")
Test3 = Test2(0)
xSum = xSum + Test3
End If
Next
SumJON = xSum
End Function
You can try the following, with the below setup of data:
Formula in B2:
=SUM(IF(ISNUMBER(SEARCH(MID($A2,LEN($A2)-3,3),B$6:B$8)),MID(B$6:B$8,SEARCH(MID($A2,LEN($A2)-3,3),B$6:B$8)+4,SEARCH(")",B$6:B$8,SEARCH(MID($A2,LEN($A2)-3,3),B$6:B$8))-SEARCH(MID($A2,LEN($A2)-3,3),B$6:B$8)-4),0)*1)
NOTE: This is an array formula and should be confirmed through CtrlShiftEnter
Drag formula down and right.
It will also work on expending the amount of names in the list AND with decimals:
Note that my system decimal point is a comma.
Breakdown:
To extract the searchvalue, JON, DOE and PRK we can make use of MID since you have a certain pattern to take into account. It would always have to be:
=MID($A2,LEN($A2)-3,3)
This value needs to be used in a SEARCH function which will return a position of our searchvalue in the A,B,C rows. Since it's an array formula it will return all these numeric positions or an error.
Therefor we need to use ISNUMBER to check if the searchvalue is actually found through SEARCH. If so it will return a TRUE and if not it will return FALSE.
Because this is part of our IFstatement, the formula will do another MID function for the TRUE values. Again we will have to make use of our searchvalue formula, but this time we know it will be in these strings and therefor we can use the numerical position as our starting position (+4, because these strings are 3 positions long + the opening paranthesis) in this second MID.
Now we have a starting position to get the lookupvalues from, we just need to know the first position of the next closing paranthesis, and we can do that through SEARCH from this exact position.
Multiplying it by 1 in the end would turn these values in true numerical lookupvalues, which finally can be summed through SUM
Et voila, we got the total of values between the paranthesis where the searchcriteria meets the current user.
I am looking for answer on how to use correctly the "FIND"formula or any other formula that can help me finding string on one cell to another.
I use the find formula to find these:
=IF(FIND(A1,B1),"yes","no")
Col1 - Col2
A,B - A,B,C = formula works well
A,C - A,B,C = formula not working. Returning "#VALUE"
Thanks for your help.
FIND only checks if the full String from Col1 is contained in Col2.
if you want to check for all items, you would have to split the String by commas somehow.
This is a quirky alternative to the above presented UDF approach, if you can accept limitations to keeping the "relative distance" between the pattern and function cells involved. My approach involves defining Names for predefined formulas (containing relative cell references).
In the following sample code I assume that I want to use the find formula in cell C2 and my comma separated pattern is stored in the neighbouring cell left to it (relative!).
Now I can define the following "names" (press <cntrl> <F3>).
one: =LEFT(Sheet1!B2;FIND(",";Sheet1!B2)-1)
oneR: =MID(Sheet1!B2;FIND(",";Sheet1!B2)+1;LEN(Sheet1!B3))
two: =LEFT(oneR;FIND(",";oneR & ",")-1)
three: =MID(oneR;FIND(",";oneR & ",")+1;LEN(oneR))
You can extend the sample if you are interested in more than three parts. In the formula cell (C2, always the one directly to the right of the pattern cell) I can now write
=IFERROR(AND(FIND(one;D2);FIND(two;D2);FIND(three;D2));0;1)
to check whether all three words of the pattern occur in the target cell string (D2). The result is 1 if all three patterns are found, otherwise it returns 0.
If your okay with using VBA, above can be achieved using function, you can use following code as UDF.
Function FindExistence(firstStr As String, secStr As String) As String
Dim firstArr() As String, secArr() As String
Dim i As Long, j As Long
Dim fElem As Variant, sElem As Variant
Dim isExist As Boolean
firstArr = Split(firstStr, ",") 'comma separated values of first cell into array
secArr = Split(secStr, ",") 'comma separated values of second cell into array
For Each fElem In firstArr 'loop through each element in array of first array
isExist = False 'set boolean variable to False
For Each sElem In secArr 'loop through each element in array of second array
If fElem = sElem Then 'check if elements in both array are equal
isExist = True 'if elements are equal exit loop, check next element
Exit For
End If
Next sElem
If Not isExist Then Exit For 'if isExist is false exit all loop
Next fElem
If Not isExist Then 'if isExist is false return No else Yes
FindExistence = "No"
Else
FindExistence = "Yes"
End If
End Function
See image for reference.
On how to create function see this.
Instead of using two Exit For you can also use GoTo.
Here's a no-column-helper approach and guaranteed zero VBA.
I use an array formula as shown below:
= LEN(SUBSTITUTE(A2,",",""))
= SUM(
--ISNUMBER(
MATCH(
MID(
SUBSTITUTE(A2,",",""),
ROW(INDIRECT("1:"&LEN(SUBSTITUTE(A2,",","")))),
1),
MID(
SUBSTITUTE(B2,",",""),
ROW(INDIRECT("1:"&LEN(SUBSTITUTE(B2,",","")))),
1),
0)
)
)
How does it work?
First, we need a way to get all the characters in your string to an array so that we can check it one by one for matches. We do that using the formula:
MID(
SUBSTITUTE(A2,",",""),
ROW(INDIRECT("1:"&LEN(SUBSTITUTE(A2,",","")))),
1)
General logic is MID(<text>,<start_num>,<num_chars>) where instead of assigning one numerical value in <start_num>, we assign an array of numbers using ROW(INDIRECT("1:"&LEN(SUBSTITUTE(A2,",","")))). In a straightforward form, that is ROW([reference]) (e.g. ROW(1:10) gives us array of numbers 1 to 10, an array with 10 elements). But to make this formula work, we need to make sure that the size of our array is equal to the number of characters we are working on excluding , commas. So we eliminate the comma first by using SUBSTITUTE(A2,",","") and get the length of the resulting string in memory. For example if you have text A,C in cell A2 then SUBSTITUTE function will yield AC (in memory) and getting the length of that using LEN returns 2. Then we use that returned value as boundary for our array. We simply concatenate it with "1:" and to make it return a valid reference, we use INDIRECT function.
We repeat above for the string we search for matches (e.g. cell B2 contains A,B,C, we need to put that in an array too)
Conduct search using MATCH function. Here, we use the array of values we have generated using the 2 steps above. MATCH(<lookup_value>,<lookup_array>,[match_type]) is the general form. Fortunately, we can also use an array of values for <lookup_value> argument instead of a single value. So we use the array we yield in step 1 above as our lookup_values.
Next is we confirm how many matches we have using ISNUMBER. MATCH function returns a numerical value (which is the position of the matched value in the array) so we simply evaluate that using ISNUMBER. This returns an array of Boolean values {TRUE,FALSE,TRUE} but since we need to count it somehow, we use double negation -- to convert it to numbers which yields {1,0,1}.
Final step is just use SUM function to count the number of matches and compare it to the length of the search string without the commas. We confirm the formula using Ctrl+Shift+Enter
Alternatively, you can use an AND function in the final step to check if all matches are found.
Edit1: Formula using AND to directly evaluate if all matches are found.
= AND(
ISNUMBER(
MATCH(
MID(
SUBSTITUTE(A2,",",""),
ROW(INDIRECT("1:"&LEN(SUBSTITUTE(A2,",","")))),
1),
MID(
SUBSTITUTE(B2,",",""),
ROW(INDIRECT("1:"&LEN(SUBSTITUTE(B2,",","")))),
1),
0)
)
)
So this will return TRUE if all characters in cell A2 is found in all character set in cell B2. Now this works for uniform comma separated strings only (e.g A,K,E,G to be searched in A,B,C,D,E,F,G,H,I,J,K). This will fail on non-uniform comma separated strings (e.g. A,BC,D,EFG,H,I,J then trying to find BC,I). I hope this gets you going.
In Above excel sheet i need a formula which will extract that particular column name where "1" is present and enter that name in the corresponding cell. Example- In above Image each row corresponding to col "Tags" contain the column name whose corresponding cell contain "1".
For a 4 column example you can use this:
=SUBSTITUTE(TRIM(IF(A2,$A$1,"")&" "&IF(B2,$B$1,"")&" "&IF(C2,$C$1,"")&" "&IF(D2,$D$1,""))," ",", ")
For 7 columns, just add additional IF statements inside the TRIM following the same pattern.
The formula relies on 1=TRUE to keep it short. The result each IF is followed by a space. The TRIM gets rid of extra spaces left when no 1 occurs. Finally SUBSTITUTE converts into , so you get a comma delimited list.
Note that:
TRIM strips extra spaces from text, leaving only single spaces between words and no space characters at the start or end of the text.
couldn't find a non vba solution for concatenating a range. So here is a UDF
Function conCatRange(ByVal criteriaRange As range, _
ByVal criteria As String, _
ByVal conRange As range, _
ByVal separator As String) As String
Dim c As range
conCatRange = ""
For i = 1 To conRange.Columns.Count
If (criteriaRange(1, i) = criteria) Then
conCatRange = conCatRange & conRange(1, i) & separator
End If
Next i
conCatRange = Left(conCatRange, Len(conCatRange) - 1)
End Function
criteriaRange = Range you want check for 1s,
criteria = 1 (in your case)
conRange = header range
separator = ","
if you want to add summary and parameter descriptions see this link
How to put a tooltip on a user-defined function
Supposing your header range is A-K,
You could you use this formula from L2 onwards:
=IF(MID(TRIM(IF(A2=1,A$1,"")&IF(B2=1,", "&B$1,"")&IF(C2=1,", "&C$1,"")&IF(D2=1,", "&D$1,"")&IF(E2=1,", "&E$1,"")&IF(F2=1,", "&F$1,"")&IF(G2=1,", "&G$1,"")&IF(H2=1,", "&H$1,"")&IF(I2=1,", "&I$1,"")&IF(J2=1,", "&J$1,"")&IF(K2=1,", "&K$1,"")),1,2)=", ",MID(TRIM(IF(A2=1,A$1,"")&IF(B2=1,", "&B$1,"")&IF(C2=1,", "&$C$1,"")&IF(D2=1,", "&D$1,"")&IF(E2=1,", "&E$1,"")&IF(F2=1,", "&F$1,"")&IF(G2=1,", "&G$1,"")&IF(H2=1,", "&H$1,"")&IF(I2=1,", "&I$1,"")&IF(J2=1,", "&J$1,"")&IF(K2=1,", "&K$1,"")),3,1000),TRIM(IF(A2=1,A$1,"")&IF(B2=1,", "&B$1,"")&IF(C2=1,", "&C$1,"")&IF(D2=1,", "&D$1,"")&IF(E2=1,", "&E$1,"")&IF(F2=1,", "&F$1,"")&IF(G2=1,", "&G$1,"")&IF(H2=1,", "&H$1,"")&IF(I2=1,", "&I$1,"")&IF(J2=1,", "&J$1,"")&IF(K2=1,", "&K$1,"")))
This formula has a limit of 1000 character in column by the mid function. and it repeats 3 times the same formula because of the separator: ", " but if you dont mind the result format you can short it to one. just fill put 1 in columns and the column name will appear.
Hope this helps.
I looking for a macro which shall find the duplicate entries in the other column.
For example, the spreadsheet I've has more than 300 entries in both column A and B and the values assorted. Now, I need to find out the duplicate entries between the columns. Like westford xxxx there in the column B or not? Please help.
Column A Column B
WestFord xxxx 1.1/2.2 1.50 Direct Link
Direct Link 1.1/2.3 1.55 Westford xxxx
You can use the "Match(Lookup_Value, Lookup_Array, [Match_type])" function. The function returns a number, if the value is found and "N/A" if it is not. First check column B for each element in column A. Then check Column A for each element in Column B.
If the Match function isn't enough, since you have partial matches, you could write a user defined function that identifies whether a value exists in the array. Below is an example of a function that checks the first characters for matching.
Function StartsWith(InputStr As String, InArr As Range, Optional Chars As Integer = 5) As Boolean
Dim i As Integer
Dim compStr As String
Dim foundFlag As Boolean
For i = 1 To InArr.Rows.Count
If Len(InputStr) > Chars Then 'we check to make sure inputstr isn't longer
compStr = Left(InputStr, Chars) 'than then the number of characters we need
Else
compStr = InputStr 'if it is too long, then compare the whole thing
End If
If compStr = Left(InArr(i, 1), Len(compStr)) Then
foundFlag = True
Exit For
End If
Next i
StartsWith = foundFlag
End Function
If this code in a module in an open spreadsheet, you can use StartsWith like any other function. If you want further automation (like deleting the cells if a duplicate is found, you can write a sub to do that also!
The internet is FULL of VBA tutorials like this one.
Try a Google Search
I am trying to "Unconcatenate" a string in Excel 2010. Yes, I know that is not a real word. So pretty much; I have a cell that can not be split into multiple columns the cell looks like this:
Item 1, Item 2, Item 3
Now this cell may have 0-? items. I am wanting to compare that one cell against a column in another sheet. I believe I would need to use the match function to do this, but I need that first cell to be converted into an array in a function with the delimiter as the comma.
So far I have =MATCH(Each item in cell, SHEET2!A:A, 0)
Any help would be nice. I am aware of =Left and =Right, but I do not think these will work because the number of items in each cell may not be the same. Thanks
Edit:
Detailed discription:
In my first sheet I have a dropdown box. When you choose items it does a vlookup on sheet 2 on this item. When this happens I want it to also check if cell E in that row (item 1, item 2, item 3) match any of the individual cells in a column in sheet 3
The following code exposes VBA's Split function for worksheet use--it returns a row array of items that have been split using a specified delimiter. For example, if cell A1 contained the text "Item 1,Item 2"), EXPLODE(A1,",") would return an array with elements "Item 1" and "Item 2".
Function EXPLODE(str As String, Optional delimiter As Variant) As Variant
If IsMissing(delimiter) Then
delimiter = " "
End If
EXPLODE = Split(str, delimiter)
End Function
It is an array function. To use the returned elements in the spreadsheet:
Select the cells in which you want the "exploded" items show
Enter the function specifying the cell with the source string (or reference to the cell which contains the source) and the delimiter on which the split will be done
Complete the entry using the Control-Shift-Enter key combination.
Alternatively, individual elements can be chosen using the INDEX function--=INDEX(EXPLODE(A1,1,2) would return "Item 2" using the previous example. (Given a range or array, the INDEX function returns the value in the ith row and jth column.) This usage does not require the formula to be entered as an array formula.
For your use case, a combination with other functions would be in order. You have a string with multiple items of the form "aa, bb, cc" (the result of a VLOOKUP) and want to determine whether any of the elements of this string can be found as individual items in any of the cells in column A. You want a function that will return True if all of the elements are found, and False otherwise. The following formula achieves that result:
=SUM(SIGN(IFERROR(MATCH(TRIM(EXPLODE(D1,",")),$A:$A,0),0)))=COUNTA(EXPLODE(D1,","))
It is an array formula and needs to be entered with Control-Shift-Enter. Note that I used the contents of cell D1 in lieu of your lookup value. The TRIM function strips out any extraneous spaces between the elements of the string with multiple items.
(not really an answer, just trying to figure out what the question is)
Sheet 1 has a dropdown box with a number of items in, the selected item is used in a vlookup() looking at a table in sheet 2.
Sheet 2 has 2(+) columns, one which is an index used for a vlookup and the other which contains delimited lists.
Sheet 3 has 1(+) columns, each row has a value that may correspond to an item in one of the delimited lists in sheet 2.
When an item is selected in the dropdown box on sheet 1 I want to lookup the corresponding list in sheet 2 (using the vlookup) and then see if any of the items in that list exist in sheet 3.
Is this what your trying to do? If yes, what is the result of this search?
Boolean: True-Some matches found!, False-No Matches
Number: I found this many results
No? :(
Update
Doing that with just worksheet functions would be fairly tricky!
VBA is a much better choice. (the final step atleast)
Add the following code to a new module (not a worksheet or workbook module) and it will be available as a UDF on your worksheets.
This function takes a string (which is your delimited list) it is exploded inside the function so you dont have to worry about doing it.
I've not tested it, but in theory it should allow you to pass it a list. The function then should check sheet 3 for you and return true/false depending on weather the items are found or not.
I know that you've found an answer, but here is a working and slightly faster version of my function.
Public Function ValidateList(ByVal Target As Range) As Boolean
Dim Sheet As Worksheet: Set Sheet = ThisWorkbook.Worksheets("Sheet3") ' Check Sheet3 is the correct sheet
Dim List As Variant: List = Sheet.UsedRange.Columns("A").Value2 ' Set Column(A) to correct column
Dim Items() As String: Items = Split(Target.Value2, ",")
Dim Item As Long
Dim Key As String
Dim Result As Boolean: Result = False
Dim Search As Object: Set Search = CreateObject("Scripting.Dictionary")
For Item = LBound(Items) To UBound(Items)
Search.Add Trim(Items(Item)), False
Next Item
If Search.Count > 0 Then
' Target List has 1+ Items
For Item = LBound(List, 1) To UBound(List, 1)
Key = Trim(List(Item, 1))
If Search.Exists(Key) = True Then
Search.Remove Key
End If
If Search.Count = 0 Then
Result = True
Exit For
End If
Next Item
Else
' Target List is Empty
' Optionally set result to True here if empty list should return True
' Defaults to False
End If
ValidateList = Result
End Function