(Excel) Pick formula criteria/logical etc from another cell(reference) - excel

Im writing an If+Or function and would like to use several cell references for the different Logicals in the function, instead of writing each logical statements in the original if+or function. Any ideas of how to solve this? Hope im not too unclear here..
As example: instead of writing =If(or(A1=A2,A3=A4),A1,0) I would like to write out all different logical values in a list of cells, and the just write the original if+or formula like this: =IF(OR(B1),A1,0) where B1 contains the text "A1=A2,A3=A4"
Thanks for any help on this!

You can use the INDIRECT function.
For example, if the value in cell A6 is 10, INDIRECT("A6") = 10.
So basically you can write INDIRECT("A6")=INDIRECT("A7") instead of the A1=A2 condition in your IF formula.
If you want to have "A1=A2" in one cell, you can use LEFT and RIGHT.
Here is an example: https://docs.google.com/spreadsheets/d/157tRicA55TFKKOi86yYBQScnjaQE6fYxaCHFdZx4uUM/edit?usp=sharing
PS: this solution is for Google Sheets so the solution might differ a little if you're using Excel but that should work for Excel too.

You CANNOT Have It All
Instead of using =IF(OR(B1),A1,0) you have to use
e.g. =IFOR(B1,A1) (I prefer "" instead of 0, sorry)
or =IFOR(B1,A1,0) if you (prefer 0 instead of ""),
or change the ElseValue in the declaration to 0,
then you can use =IFOR(B1,A1) to get 0.
Function IFOR(IfOrCell As Range, ThenCell As Range, _
Optional ElseValue As Variant = "", _
Optional SplitDelimiter As String = ",") As Variant
'Description:
'Pending...
'Recalculation
Application.Volatile
'Variables
Dim arrIfOr As Variant
Dim iIfOr As Integer
Dim blnIfOr As Boolean
'The Array: The Split
If InStr(IfOrCell.Value2, SplitDelimiter) = 0 Then Exit Function
arrIfOr = Split(IfOrCell.Value2, SplitDelimiter)
'An Additional Split Introducing the Boolean
For iIfOr = LBound(arrIfOr) To UBound(arrIfOr)
If InStr(arrIfOr(iIfOr), "=") <> 0 Then
If Range(Split(arrIfOr(iIfOr), "=")(0)).Value2 _
= Range(Split(arrIfOr(iIfOr), "=")(1)).Value2 Then
blnIfOr = True
Exit For
End If
End If
Next
'Output
If blnIfOr = True Then
IFOR = ThenCell.Value2
Else
IFOR = ElseValue
End If
End Function

Related

VBA - Parsing Date from Free Form Text String

I am attempting to parse out clean target DATES from cells populated with free form TEXT STRINGS.
ie: TEXT STRING: "ETA: 11/22 (Spring 4.5)" or "ETA 10/30/2019 EOD"
As you can see, there is no clear standard for the position of the date in the string, rendering LEFT or RIGHT formulas futile.
I tried leveraging a VBA function that I found which essentially breaks up the string into parts based on spaces in the string; however it has not been working.
Public Function GetDate(ResNotes As String) As Date
Dim TarDate As Variant
Dim part As Variant
TarDate = Split(ResNotes, " ")
For Each part In ResNotes
If IsDate(part) = True Then
GetDate = part
Exit Function
End If
Next
GetDate = "1/1/2001"
End Function
I'm referring to the cells with text strings as "ResNotes", short for "Resolution Notes" which is the title of the column
"TarDate" refers to the "Target Date" that I am trying to parse out
The result of the custom GETDATE function in Excel gives me a #NAME? error.
I expected the result to give me something along the lines of "10/30/2019"
Unless you need VBA for some other part of your project, this can also be done using worksheet formulas:
=AGGREGATE(15,6,DATEVALUE(MID(SUBSTITUTE(A1," ",REPT(" ",99)),seq_99,99)),1)
where seq_99 is a named formula and refers to:
=IF(ROW($A$1:INDEX($A:$A,255,1))=1,1,(ROW($A$1:INDEX($A:$A,255,1))-1)*99)
*seq_99 generates an array of numbers {1;99;198;297;396;495;...
Format the cell with the formula as a Date of some type.
If there are no dates, it will return an error which you can either leave, or wrap the function in an IFERROR(your_formula,your_error_message)
Algorithm
Split the cell on the spaces
Replace each space with 99 spaces
Using the MID function, return an array of substrings 99 characters long
Apply the DATEVALUE function which will return either an error (if the substring is not a date) or a date serial number.
Since dates in Excel are serial numbers since 1/1/1900, we can use the AGGREGATE function to pick out a value, and ignore errors.
If you are getting #NAME then the code is not stored in a general module. It should NOT be in a worksheet module or ThisWorkbook module.
Also there are few errors in the code. Split returns a String Array. And since IsDate returns TRUE/FALSE the = True is not needed.
As per #MathieuGuindon we can change the string to a date in the code if found and return an error if not. For that we need to allow the return to be a variant.
Public Function GetDate(ResNotes As String)
Dim TarDate() As String
Dim part As Variant
TarDate = Split(ResNotes, " ")
For Each part In TarDate
If IsDate(part) Then
GetDate = CDate(part)
Exit Function
End If
Next
GetDate = "1/1/2001"
'Instead of a hard coded date, one can return an error, just use the next line instead
'GetDate =CVErr(xlErrValue)
End Function
Approach isolating the date string via Filter function
Just for fun another approach demonstrating the use of the Filter function in combination with Split to isolate the date string and split it into date tokens in a second step; finally these tokens are transformed to date using DateSerial:
Function getDat(rng As Range, Optional ByVal tmp = " ") As Variant
If rng.Cells.count > 1 Then Set rng = rng.Cells(1, 1) ' allow only one cell ranges
If Len(rng.value) = 0 Then getDat = vbNullString: Exit Function ' escape empty cells
' [1] analyze cell value; omitted year tokens default to current year
' (valid date strings must include at least one slash, "11/" would be interpreted as Nov 1st)
tmp = Filter(Split(rng.Value2, " "), Match:="/", include:=True) ' isolate Date string
tmp = Split(Join(tmp, "") & "/" & Year(Now), "/") ' split Date tokens
' [2] return date
Const M% = 0, D% = 1, Y& = 2 ' order of date tokens
getDat = VBA.DateSerial(Val(tmp(Y)), Val(tmp(M)), _
IIf(tmp(D) = vbNullString, 1, Val(tmp(D))))
End Function

VBA VLookup Method not working for both numbers and strings

I have a workbook full of product codes and names. Contained within a form are various text boxes where a user can enter a code and its corresponding label will update with the name found in the workbook. Each text box runs the following sub when changed
Private Sub FindItem(x As Long)
Dim Name As Variant
Name = Application.VLookup(AddStockForm.Controls("Code" & x).Text, Sheet1.Range("B:C"), 2, False)
If IsError(Name) Then
AddStockForm.Controls("Name" & x).Caption = "Unknown Code"
Else
AddStockForm.Controls("Name" & x).Caption = Name
End If
End Sub
The sub takes the user input in the target box (e.g. Code1) and finds the corresponding name and writes it to the label (e.g. Name1). HOWEVER, the product codes are either strings, alphanumeric and plain text, OR numbers. For stupid reasons beyond my control, some codes have to be numbers, others have to contain letters.
This code works PERFECTLY for any code with a character in it (MYCODE or 500A) but not numbers, it writes "Unknown code" for any number, and they are in the lookup range. I have searched around stackoverflow and answers suggest declaring as variants, I've done this, even by assigning Controls().Text as a variant before using it in VLookup. I suspect the problem is
AddStockForm.Controls("Code" & x).Text
is a string. But I cannot convert to an INT because the user input might be a number or string.
Any ideas?
One thing you can do is to create a separate function which has the separate parts you want to do. In this instance, we are checking the input value first. If this is numerical we want to try doing the lookup as a string, then as a number if that fails. If the input value is not numerical we can go ahead and do the lookup as normal.
Public Function lookupStringOrInt(inputValue As Variant, tableArray As Range, colIndexNum As Long, Optional rangeLookup As Boolean) As Variant
If IsNumeric(inputValue) Then
lookupStringOrInt = Application.IfError(Application.VLookup(inputValue & "", tableArray, colIndexNum, rangeLookup), Application.VLookup(inputValue * 1, tableArray, colIndexNum, rangeLookup))
Else
lookupStringOrInt = Application.VLookup(inputValue, tableArray, colIndexNum, rangeLookup)
End If
End Function
You can then call this in your code with the line
name = lookupStringOrInt(AddStockForm.Controls("Code" & x) & "", Sheet1.Range("B:C"), 2, False)
If the value you are looking for does not exist, the function will return 'Error 2042'. You can choose to handle this however you like.

chanaging an IF and Weekday formula into a vba

I need help to change the following function into VBA code. This will be part of a larger code.
IF((WEEKDAY($B12)=7),$I12,"")
There are probably more than 5 ways to do what you want, depending on what exactly do you need. One of these ways is to build a simple custom formula like this:
Public Function changingIfAndWeekday() As Variant
Application.Volatile
If Weekday(Range("B12")) = 7 Then
changingIfAndWeekday = Range("I12")
Else
changingIfAndWeekday = ""
End If
End Function
You could also do it like so (if you want the result on cell C12):
Sheet1.range("C12").value = "=IF(Weekday(Sheet1.range("B12").value = 7),Sheet1.range("I12").value,"")
You could also do it like so (if you want the result on a variable):
Variable = "=IF(Weekday(Sheet1.range("B12").value = 7),Sheet1.range("I12").value,"")

Check a range of text against a text string and return "yes" or "no"

I have a list of text strings (titles) and a list of "bad" keywords I want to identify within those titles.
The problem I've run into is how to identify individual text within the string. Several simple formulas can do this for an exact match, but I need it to identify words within the titles.
For example, in "Software sales representative" I need it to recognize "software" is on the list and return the word "bad".
I've browsed other questions on here and there's nothing that answers this specifically on Stacked.
The other similar questions have said this can be done with a asterisk/ wildcard, but this does not work. I know conditional formatting can do this, but I need a formula since I have over 100 "bad" keywords.
This is as far as I've gotten:
=IF(COUNTIF(K:K,"*"&E2&"*"),"Bad","Okay")
But this formula still only returns exact matches.
Column E is the titles. Column K is the keywords.
You will need to use this formula:
=IF(SUMPRODUCT(--(ISNUMBER(SEARCH($K$1:INDEX(K:K,MATCH("zzz",K:K)),E2)))),"Bad","Okay")
Put this Function in a module and call it from a cell like this =ContainsBadwords(badwordsrange,rangetotest;false;false)
Public Function ContainsBadwords(BadWordList As range, ByVal SuspectRange As range, Optional ExactMatch As Boolean = True, Optional CaseSensitive As Boolean = True)
For Each ArrayItem In SuspectRange
If Not CaseSensitive Then ArrayItem = Strings.lcase(ArrayItem)
For Each Subwhat In BadWordList
If ExactMatch Then
test = (ArrayItem = Subwhat)
Else
test = InStr(ArrayItem, Subwhat) >= 1
End If
If test Then: ContainsBadwords = test: Exit Function
Next Subwhat
If test Then: ContainsBadwords = test: Exit Function
n = n + 1
Next ArrayItem
End Function
Returns TRUE for blasphemy or FALSE if no bad words exist.

Passing a range from one VB function to another

I'm writing a few VBA functions for work and ran into a problem that should be easy to solve, but somehow I can't manage to, despite my best attempts at finding an answer here and on Google. I wrote a function that should give me the range between two strings in a column:
Function FindRng(StartRng As String, EndRng As String) As Variant
Dim TopOfRange As Single
Dim BottomOfRange As Single
TopOfRange = WorksheetFunction.Match(StartRng, Sheets("InfCom").Range("B:B"), 0)
BottomOfRange = WorksheetFunction.Match(EndRng, Sheets("InfCom").Range("B:B"), 0)
FindRng = Range(Sheets("InfCom").Cells(TopOfRange, 2), Sheets("InfCom").Cells(BottomOfRange, 2))
End Function
So if the inputs A and B are on rows 100 and 105, it should return B100:B105. When I test this by adapting the code to read FindRng = Range(...).Address, I indeed get $B$100:$B$105.
However, when I then input the result of FindRng into a customized Index Match function, I get an error. The function is as follows:
Function subsetPBPC(rngReturn As Range, LookupValueH As Variant, TopOfRange As String, BottomOfRange As String, LookupValueV As Variant) As Variant
subsetPBPC = sPBPC(rngReturn, LookupValueH, FindRng(TopOfRange, BottomOfRange), LookupValueV)
End Function
The problem is that it seems to read the output of FindRng not as a range, but as the content of that range: when I use the Evaluate Formula tool on FindRng embedded in another formula, it shows the output of FindRng as {A,B,C,D,E} instead of $B$100:$B$105, where A to E are the contents of the cells in the range. I have the feeling the solution is really simple, but I don't see it. The functions underlying the customized Index Match function have been tested and all work like a charm.
Set instead of let. Let assigns the value of an expression to a variable. Set assigns an object reference to a variable. You want to return a reference to the range object, not return the value produced by the range object's default property.
In VBA writing
FindRng = Range(...)
is implicitly writing
Let FindRng = Range(...)
However you want
Set FindRng = Range(...)
Edit 1:
It is quite important to understand the difference between an object reference and a value in VBA. This is a similar concept to passing arguments by value or by reference. Hopefully these two links help some:
The Let statement on MSDN
The Set statement on MSDN
Edit 2:
Oh, and I guess I should touch on default properties! Some objects like range have default properties. If you treat the range as a value instead of an object, it uses the default property instead of throwing an error because it's an object not a value. In the case of range the default property is Value. So if you say A = Range("A1") what you're actually saying is Let A = Range("A1").Value when you might mean Set A = Range("A1"). So you're getting the value contained in the cell A1, instead of a range object representing that cell.
Picking up that your current code should both
use Set as per AndADM's commnet
dimension SetRng as a Range rather than Variant
you can simplify your function as below (which may save time if you are calling it repetitively)
Also, you could test for this range being Nothing (if your two strings werent found), whereas you current code will error out if either string is missing.
Function SetRng(str1 As String, str2 As String) As Range
With Sheets("infCom").Columns(2)
Set SetRng = Range(.Find(str1, , xlValues, xlWhole), .Find(str2, , xlValues, xlWhole))
End With
End Function

Resources