Which function to use when want to check if value in one column is in a range on another tab and to return he's or no (or similar output in text)
Basically I am trying to determine if an address in one table is within a range of addresses in another table and to return a yes or no (or similar)
If value B2 (table 1) is within range C2:C26 (table 2) then yes else 0 (or 1 else 0) etc
Related
Within a procedure I am writing, I check all values of different columns of a table to be equal (or not equal) to a value.
Thing is I do not know in advance how many columns I will check and what values I will check and does not want to have to ask for another array of value (like 0 and 1) to specify if the value given in the first array is to be equal or NOT equal to the table's column's value.
Here is an example:
With Workbooks(path).Worksheets(sheetName)
Set tbl = .ListObjects(tableName)
i = 1
For Each col In fColumn
colRange(i) = tblSource.ListColumns(col).DataBodyRange
i = i + 1
Next
Dim flag As Boolean
For i = 1 To .ListRows.Count
flag = True
For j = 1 To fColumn.UBound
If colRange(i).Value <> fColumnVal(i) Then ' check if stored value is equal to specific value
flag = False
Exit For
End If
Next j
If flag = True Then
' do something
End If
Next i
So fColumn is an array of string which contains the name of the column to be checked, colRange is the respective data range, fColumnVal is the value to be tested.
How I approached my problem is for each row of the table, I test the cell value of the different columns to be tested (names in fColumn) against the value of fColumnVal.
In theory that would work w/o problem. However, it could be possible that someone would like to find values which are different of the value in fColumnVal such as:
<<Find every cell which are different of value "Hello">>
My question is, is it possible to encompass the idea of check if not equal in the value of fColumnVal and not in the line of code of the condition. Something similar to the use of "<>" in Excel's formula which is equivalent <<to different of "">>.
If that does not exist I will simply add another required variable to the sub which indicates how to test the value.
Thanks.
I have Excel data in column B (cells B2:B60) that contains email addresses (such as xxx#ahcptcare.com). In column D, I need to print "APC" if the value in B contains ahcptcare.com, or UDP if the value in B contains #upschyd.com, and so on for more email addresses.
I don't know how to look for the text after the # symbol in column B and return another value in column D. I started researching on RIGHT and FIND functions in VBA but I'm at loss on this one. I think I can use and IFS and RIGHT but I'm not sure how to.
How can I do this?
You can do this with a loop and two lines of code. If you are just checking for those 2 domains then
Dim r As Range
Set r = Sheets(1).Range("B2:B60")
Dim c As Range
'Loop through each cell in the range
For Each c In r
'Check for the first domain then check for the second.
If InStr(1, UCase(c.Value), "AHCPTCARE.COM", vbTextCompare) > 0 Then c.Offset(0, 2).Value = "APC"
If InStr(1, UCase(c.Value), "UPSCHYD.COM", vbTextCompare) > 0 Then c.Offset(0, 2).Value = "UDP"
Next
I'll go over how this would work for the following example data in Excel and hopefully it will teach you how to apply similar VBA code to your own Excel data. It's been a while since I've written VBA, so there may be more efficient ways to do this.
A
B
C
D
Email
Result
john1#abc.com
john2#xyx.com
john3#qrf.com
john4#wtf.com
john5#omg.com
john6#bbq.com
First you need to determine your business logic: what is it you are trying to do? Well, you have a range of data that you want to loop over, and, based on part of the data, print a specific string/value to another column in the same row as each data point, respectively.
In my case, I have data in cells B2:B7 of column B (Email), and I want to print a value to column D (Result).
The first thing we need to do in VBA is declare some variables for the data we are going to work with:
Dim rng As Range, cell As Range 'We are running through a range of cells, so the datatype of Range is appropriate here
Dim cellVal As Variant, comparisonVal As Variant 'Each cell will have text and special characters, so I just used the catch-all datatype of Variant here
Set rng = Range("B2:B7") 'This is just my range of data
Now that we've got some variables declared, you can start on your loop now. You need to loop through each cell in your range, so that's first... and there's not much better for looping through a set range of cells than a for each ... in loop in VBA.
I typically write the entire loop 'wrapper' first and then click inside it to start writing what will happen for each iteration.
For Each cell In rng
Next cell
Next, we'll jump inside that loop and write the steps we want to loop through. After you move to be inside the loop, we need to determine the value of the cell (but only the part after the # symbol) and store that value so that it can be compared against later. It's also good practice to check whether there is an # symbol in the first place... otherwise your code may go off the rails.
cellVal = cell.Value 'We're assigning this here rather than in the original variable declaration section because its value needs to change with each cell we loop through
If InStr(1, cellVal, "#") > 0 Then 'The InStr() function checks whether a specified value exists in a string
comparisonVal = Split(cellVal, "#")(1) 'Here we assign the value we want to use for comparison by using the Split() function to give us the value after the # symbol. Note that this may not work if the # symbol appears multiple times in a cell in column B. Lookup the Split() function for further information.
Else
MgBox ("No # symbol found! Exiting loop.") 'If we don't find an # symbol in one of the rows, we should probably alert the user. Dirty source data plagues even the best of us.
End If
OK! We're almost there. Now we just need to... actually apply the right Result value in column D based on the value in our comparisonVal variable. I don't know the extent of your data or business logic, but from what you've shared in your question, it sounds like you have some predetermined Result values to put into the Result column depending on the value of the Email column (column B).
When you have several predetermined routes you want to follow based on several possible scenarios, the best way to achieve that is often a switch statement (sometimes called a case statement). The syntax for that in VBA is:
Select Case [comparison]
Case [case value 1]
Case [case value 2]
...
Case Else
End Select
In our case (no pun intended...), we'll use comparisonVal (which contains just the part of each email address after the # symbol) and assign the appropriate value to column D of the current row. To do that assignment in a relative way, we'll use the Offset() function; you can look that up later for further reading if necessary. Remember, this bit of code also goes inside the foreach loop.
Select Case comparisonVal
Case "abc.com"
cell.Offset(0, 2).Value = "APC"
Case "xyz.com"
cell.Offset(0, 2).Value = "UDP"
Case "qrf.com"
cell.Offset(0, 2).Value = "THC"
Case "wtf.com"
cell.Offset(0, 2).Value = "IDK"
Case "omg.com"
cell.Offset(0, 2).Value = "LOL"
Case Else
cell.Offset(0, 2).Value = "Unknown Value"
End Select
Run all of that code inside your subroutine or function and you'll end up with the following data result (when you start with the data in the table above):
A
B
C
D
Email
Result
john1#abc.com
APC
john2#xyx.com
UDP
john3#qrf.com
THC
john4#wtf.com
IDK
john5#omg.com
LOL
john6#bbq.com
Unknown Value
Which should get you to 100% task completion!
I have an excel file with 2 sheets
In the first one, I have two columns:
In the second, I have an SQL query that loads into Excel that looks something like this:
I have validation on the first column, limiting the value to column A, so the user can either start typing which item he wants, or use the dropdown to pick.
My issue is that I want to create validation on the second column, so that only the options applicable to that Item are available (ex. if the first column is Item 1, only options A, B, and C are available to be selected.) The SQL data is dynamic (possibly different options in Column B for each A), and new items are added and removed. The solutions I saw (ex. https://trumpexcel.com/dependent-drop-down-list-in-excel/) involved creating the two filters on two axis, but I'm not sure how that would work in my situation.
If you can create a list of unique values from the first column to use as the source for the first drop-down (e.g. like in ColD below), you can configure the second drop-down to use a "Source" formula like:
=OFFSET($B$1,MATCH(I2,$A:$A,0)-1,0,COUNTIF($A:$A,I2),1)
Where I2 here is the corresponding drop-down pointed at ColA values (see example screenshot)
Assuming your source data is sorted on ColA.
EDIT: adding a different (perhaps more flexible) approach:
Define a workbook name like "validation" which points to a VBA function which returns a range:
Corresponding function in a regular code module:
Function getOptions() As Range
Dim m As Long, c As Range, v
Set c = Application.Caller 'getOptions is called from the cell
' where the drop-down is clicked
v = c.Offset(0, -1).Value 'read cell (e.g.) to left of the one being filled
'Debug.Print v
If Len(v) > 0 Then
'do the same thing we did with the DV list Source formula above
m = Application.Match(v, c.Parent.Columns("A"), 0)
If Not IsError(m) Then
'return a range (must be contiguous)
Set getOptions = c.Parent.Cells(m, "B").Resize( _
Application.CountIf(c.Parent.Columns("A"), v), 1)
End If
End If
End Function
Finally, set up your cell validation list to point to =validation
The advantage of this approach (despite being a little more set-up and requiring a macro-enabled workbook) is that the VBA in the function can look at the context of the calling cell (via Application.Caller) and decide what range of cells to return to be shown in the drop-down.
It is a requirement that I use Excel to solve this issue.
In col A I have 0s and 1s with various quantities of 0s between the 1s. Every time a 1 appears I want the difference between two numbers given in two columns next to my binary column. However I wish to get the results from this calculation stated next to the previous 1.
I'd cope with different software, but how do I achieve this with Excel?
=IF(A4=1,OFFSET(B4,MATCH(1,A5:A$1000,0),0)-OFFSET(C4,MATCH(1,A5:A$1000,0),),"")
in D4 and copied down to suit seems to work.
Edit:
=(IF(A$=1, ,"") is as in: IF(logical_test,value_if_true,value_if_false) where value if false is (blank), expressed as "".
The value_if_true is the difference between ColumnB and ColumnC values, each ‘located’ from an OFFSET function as in =OFFSET(reference,rows,cols,height,width).
references are to the appropriate column for the row into which the formula is inserted (ie B4 and C4) from which the values required are ‘south’ by a variable amount.
MATCH, as in =MATCH(lookup_value, lookup_array, [match_type]) is to determine the extent of the offset on a case-by-case basis. In reverse order, the parameters here are match_type = 0 (to require an exact match) and lookup_array is as much of ColumnA as required. Initially chosen as up to Row1000 (by A$1000) but can be extended as far as necessary, subject to row limit for the relevant Excel version.
The first parameter lookup_value) is of course 1 since that is the flag for the rows that contain the values to be subtracted.
Without a $ between A and 5 in the MATCH functions the size of the array automatically decreases (top cell row reference increases) as the formula is copied down, hence finds the next instance (rather than the same one over and over again).
With VBA, I'd first set the formulas to show results in same line as the "ones". (Suppose I used the D column for that.)
= if(A1 = 1; B1 - C1; "")
Then, in VBA window, do the following:
Dim i as integer
Dim Filled as Collection
Set Filled = new Colleciton 'this collection will stored filled lines
'store filled lines
for i = 2 to 1000 'use your table limit
if Sheet1.Cells(i, 4).Value <> "" then 'the 4 is for D column, i for the line
Filled.Add i
end if
next
'now fill the E column with displaced values
for i = 1 to Filled.Count - 1
Sheet1.Cells(Filled(i), 5).Value = Sheet1.Cells(Filled(i+1), 5).Value
next
'please note there's a missing line (the last), it's up to you to decide how to fill it
'sorry I cannot debug this code
I'd associate that to some sheet event or to a button.
After 3 hours of searching I still didn't find an answer, here is what I am trying to do:
I am trying to fill with green any row that has WBS in it and with Red any row that has ACT in it and Blue any row that has EPR in it. It works for the first formula then when I try to add the second one every thing get messed up.
what i have understood is that you need to search a keyword in a row and if its found in any cell of that row then color it.
May be we can do it with conditional formatting but i have another idea. We can create a simple search function in Excel VBA. Something like this:
=search_row(A1:F1,"EPR")
The function will return 1 if EPR is found in any cell of specified row. Now if you create two different columns at the end of data columns, name first with WPS and second with EPR and write this function in it. Like
G1 =search_row(A1:F1,"WPS")
H1 =search_row(A1:F1,"EPR")
Drag it to end. Now sort the columns. First for WPS from higher to lower. Then color all rows having 1 in a single select. Similarly do the same with EPR (H1) column.
To use this function you can download the macro file from the following URL:
http://asimishaq.com/myfiles/SearchHighlight.xlsm
Now to run it first of all enable macros, and then re-open your data file and then open this macro file. As long as this macro file is opened you can use this function. Following is the VBA code if you want to create the macro yourself:
Function search_row(sRow As Range, Keyword As String)
Dim i As Integer
Dim Found As Integer
For i = 1 To sRow.Columns.Count
If InStr(1, LCase(sRow.Cells(1, i)), LCase(Keyword)) > 0 Then
search_row = 1
End If
Next
End Function
I had a go at making a function similar to asim-ishaq to determine if your search term exists in the row for fun :) then tried to apply it to highlighting rows, turns out I dont know how to use conditional formatting very well! Figured it out in the end, hopefully I've explained it well enough.
With this you will have to have (one) extra column at the end of your data to contain the result.
It might be possible to not require the extra column by putting the function inside the conditional formatting, however I couldn't get it to work (didn't try very hard). This isn't a great loss as it's much simpler to edit the formula if its on the workbook, instead of having to go through each conditional rule to edit it, should you need to edit it in the future.
To get the formatting to work you will need to create a number of rules (one per keyword)
You want to create a rule of the type shown below, in the formula box you need something along the lines of: =INDIRECT("D" & ROW())=0 where D is the column containing the result of the function below and 0 is the index of the keyword you're highlighting.
In my example, the formula in the D Column is: =SearchRow(An:Cn,"ABS","KBS","JBS") (where n is the row the formula is on)
Set the formatting as desired then press OK, when you return to the rule manager you will need to update the Applies to value, which should be a range that covers all the data you want to highlight. In my example it was $A$1:$C$3
My function below takes 2+ Arguments, The first is the range to search. The second (and any subsequent ones) are search terms.
The function will return a number. -1 for no matches and 0+ for the found search term. The number depends on the position in the arguments.
A1 = "ABS"
B1 = "SBA"
A2 = "SBA"
B2 = "ABS"
A3 = ""
B3 = ""
C1 = "=SearchRow(A1:B1, "ABS", "SBA")"
C2 = "=SearchRow(A2:B2, "ABS", "SBA")"
C3 = "=SearchRow(A3:B3, "ABS", "SBA")"
C1 > 0
C2 > 1
C3 > -1
The function will always return the first result, searching left to right comparing each cell to the Keywords in order. Using my example, if a cell contained "SBA ABS" the result would be 0 (for ABS). I guess your cells will probably only contain one keyword though so this shouldn't be a problem?
Public Function SearchRow(ByVal Row As Range, ParamArray Keyword() As Variant) As Integer
Dim Column As Integer
Dim Value As String
Dim Index As Integer
Dim Result As Integer
For Column = 1 To Row.Columns.Count
Value = LCase(Row.Cells(1, Column))
Result = -1
For Index = LBound(Keyword) To UBound(Keyword)
If InStr(1, Value, LCase(Keyword(Index))) > 0 Then
Result = Index
Exit For
End If
Next Index
If Result > -1 Then
Exit For
End If
Next Column
SearchRow = Result
End Function