If function giving me problems - vba

My value keeps becoming false instead of the values i've added even when i enter Male. anyone has any idea?
If gender = "Male" Then
Sheet2.Range("A2").Value = "he" & Sheet2.Range("A3").Value = "him" & Sheet2.Range("A4").Value = "his"
Else
Sheet2.Range("A2").Value = "she" & Sheet2.Range("A3").Value = "her" & Sheet2.Range("A4").Value = "her"
End If

You can't assign a value two time in the same statement. Any = character after the first is treated as a logical operator (is equal to), not as an assignment.
So if you have:
a = b = c
VBA treats the statement as: Assign to A the results of "Is b equal to c".
It gets more complicated if you have three equal signs in a statement, but it won't be what you want in any rate.
Additionally, the & operator is for string concatenation. so
a = "top" & "hat"
will assign the string "tophat" to a.
You might want to use the : operator instead of &. That separates statements on a single line. So try this:
If gender = "Male" Then
Sheet2.Range("A2").Value = "he" : Sheet2.Range("A3").Value = "him" : Sheet2.Range("A4").Value = "his"
Else
Sheet2.Range("A2").Value = "she" : Sheet2.Range("A3").Value = "her" : Sheet2.Range("A4").Value = "her"
End If
However, I would urge you not to use : here. There's really no need, and I think it makes the code harder to read. I'd suggest:
If gender = "Male" Then
Sheet2.Range("A2").Value = "he"
Sheet2.Range("A3").Value = "him"
Sheet2.Range("A4").Value = "his"
Else
Sheet2.Range("A2").Value = "she"
Sheet2.Range("A3").Value = "her"
Sheet2.Range("A4").Value = "her"
End If

Rich Holton gave you the reason why your code didn't work
Here I propose you some different techniques
Option Explicit
Sub main()
Dim valsArray As Variant '<--| declare a Variant variable to hold an array
Dim gender As String
gender = "Male" '<--| gender initialization for testing purposes
If gender = "Male" Then
valsArray = Array("he", "him", "his") '<--| fill your Variant with values corresponding to the 'gende'r value
Else
valsArray = Array("she", "her", "her") '<--| fill your Variant with values corresponding to the 'gende'r value
End If
Sheet2.Range("A2:A4").Value = Application.Transpose(valsArray) '<--| write all values in one shot (you need 'Transpose()' to change the "horizontal" Variant array to a "vertical" one and fit the range you're fill values of
End Sub
you can also shorten it down even more by using IIf() function:
Option Explicit
Sub main()
Dim gender As String
gender = "Male" '<--| gender initialization for testing purposes
Sheet2.Range("A2:A4").Value = Application.Transpose(IIf(gender = "Male", Array("he", "him", "his"), Array("she", "her", "her")))
End Sub

Related

Set Paste Destination as Last Used Row on Another Sheet

Thanks so much for taking a second to help me a bit! I'm working on a project at the moment, and part of it has me stuck. I'm not terribly proficient with VBA, so its entirely possible that I'm missing something very obvious here.
Goals:
Copy a non continuous group of cells (eg. d69,d70,d72,d73,g92,g93, etc.) and paste them to another (This time Continuous) range of cells on another sheet, in the row below the last used row.
Context:
Im creating a database of information filled in from a "User Form" on sheet 1. When the user clicks a macro linked button, the data is copied over to sheet 2 as a new entry.
Thoughts:
I have been thinking that it may be easier to set a variable to the value of the last cell used in sheet 2, then use something like a range("b" & "aa").pastespecial xlPasteValues for each cell that needs to be copied over. However I cant figure this out, or find what I need to do to achieve this. Any help would be greatly appreciated! Thanks so much.
If you have any questions, or need clarification, let me know! Thanks!
See document link below:
Working File
Another way you could achieve this which might be a bit easier is to use a helper column to 'store' your input values and then put that range into an array to write directly to your database sheet.
Assuming your helper columns are on a new sheet named "Helper", data input on a sheet named "BBU Quote Entry" and the data is moving to BBU Quote Database.
Sub BBUEntryToDatabaseUsingHelper()
Dim UserInputsArray() As Variant
Dim HelperRange As Range
Dim Destination As Range
Dim LastBBUDatabaseRow As Long
Dim LastHelperRow As Long
With ThisWorkbook.Sheets("Helper")
LastHelperRow = .Cells(Rows.Count, 2).End(xlUp).Row
Set HelperRange = .Range("B2:B" & LastHelperRow)
End With
UserInputsArray() = HelperRange.Value
With ThisWorkbook.Sheets("BBU Quote Database")
LastBBUDatabaseRow = .Cells(.Rows.Count, 2).End(xlUp).Row + 1
Set Destination = .Cells(LastBBUDatabaseRow, 2)
Set Destination = Destination.Resize(1, UBound(UserInputsArray, 1))
Destination.Value = Application.Transpose(UserInputsArray)
End With
End Sub
Input Form:
Helper columns:
This works simply by referencing the relevant cell from the input sheet on the helper sheet.
E.g. The "Customer Company" value is in cell D6 on sheet BBU Quote Entry so the helper column has ='BBU Quote Entry'!D6
For the "Hazardous" reference I found the cell your Form Control OptionButtons are linked to (E74 on Sheet BBU Quote Entry and used =IF('BBU Quote Entry'!E74 = 1, "Hazardous",IF('BBU Quote Entry'!E74 = 2,"Non-Hazardous","Not Specified"))
As you have some custom formatting, for example the "Desired Clearance" value formats the input as #### Inches, the reference only returns the value entered and not the formatting - You can look further into resolving that but in the mean time I added a string after the value reference, e.g. for "Desired Clearance" =('BBU Quote Entry'!D15) & " Inches".
With the data being held in the correct order for the output onto your "BBU Quote Database" sheet, we can simply put the range from the "Helper" sheet directly into an Array(), and then write the array to the correct range in "BBU Quote Database".
This is what the output looks like:
This is the way I'd probably go about it. Much less code, easier to maintain as both ranges are set dynamically so if you end up adding more inputs to your form, just include their reference on the helper sheet and the code will automatically include the new values the next time you run the code.
After your commented clarification and the addition of the Workbook to your question, I've edited this Answer to reflect these updates.
Assuming you know the cell address for each value on sheet1 AND the cell address are constant.
I've written a Subroutine to capture the values of your BBU Quote Entry form and write them to your BBU Quote Database range. I've added this to Module4.
It should be noted, the code only works with the Basic Information section of your form AND the 2 option buttons for Hazardous or Non-Hazardous using a function ReturnFormControlCaption. You can put in the hard yards for the rest of the data (more or less just copy paste, rename variables, adjust the range values and add the variables to the array of course).
Sub BBUEntryToDatabase()
Dim CustCompany As String
Dim CustName As String
Dim CustLocation As String
Dim CMTRep As String
Dim QuoteNo As String
Dim QuoteDate As String
Dim Hazard as String
With ThisWorkbook.Sheets("BBU Quote Entry")
CustCompany = .Range("D6").Value
CustName = .Range("D8").Value
CustLocaction = .Range("D10").Value
CMTRep = .Range("G6").Value
QuoteNo = .Range("G8").Value
QuoteDate = .Range("G10").Value
Hazard = ReturnFormControlCaption("BBU Quote Entry", "HazardousButton", "NotHazardousButton")
End With
Dim BBUArray As Variant
'The Array is assigned in order of your headings on "BBU Quote Database" sheet
BBUArray = Array(QuoteNo, CustCompany, CustName, CustLocation, CMTRep, QuoteDate, _
"Clearance", "Height", "Material", "Density", Hazard)
Dim Destination As Range
Dim LastRow As Long
With ThisWorkbook.Sheets("BBU Quote Database")
LastRow = .Cells(.Rows.Count, 2).End(xlUp).Row + 1
Set Destination = .Cells(LastRow, 2)
Set Destination = Destination.Resize(1, UBound(BBUArray, 1) + 1) ' + 1 as the array is 0 based (whereas columns start at 1).
Destination.Value = BBUArray
End With
End Sub
Here is a screenshot of my data entry
And the output on "BBU Quote Database" (after 3 tests with the same inputs)
I'm not very familiar with Form Controls as I usually use ActiveX Controls which I find a bit easier to use with VBA - I'd assume there is probably a much cleaner way to deal with the OptionButtons.
The ReturnFormControlCaption() function:
Function ReturnFormControlCaption(ByVal SheetNameTheControlIsOn As String, ByVal FirstFormControlName As String, _
Optional ByVal SecondFormControlName As String, Optional ByVal ThirdFormControlName As String, _
Optional ByVal FourthFormControlName As String, Optional ByVal FifthFormControlName As String, _
Optional ByVal SixthFormControlName As String) As String
With ThisWorkbook.Sheets(SheetNameTheControlIsOn)
If SecondFormControlName = "" Then
If .Shapes(FirstFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(FirstFormControlName).OLEFormat.Object.Caption
Else
ReturnFormControlCaption = "Not Specified"
End If
ElseIf ThirdFormControlName = "" Then
If .Shapes(FirstFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(FirstFormControlName).OLEFormat.Object.Caption
ElseIf .Shapes(SecondFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(SecondFormControlName).OLEFormat.Object.Caption
Else
ReturnFormControlCaption = "Not specified"
End If
ElseIf FourthFormControlName = "" Then
If .Shapes(FirstFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(FirstFormControlName).OLEFormat.Object.Caption
ElseIf .Shapes(SecondFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(SecondFormControlName).OLEFormat.Object.Caption
ElseIf .Shapes(ThirdFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(ThirdFormControlName).OLEFormat.Object.Caption
Else
ReturnFormControlCaption = "Not specified"
End If
ElseIf FifthFormControlName = "" Then
If .Shapes(FirstFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(FirstFormControlName).OLEFormat.Object.Caption
ElseIf .Shapes(SecondFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(SecondFormControlName).OLEFormat.Object.Caption
ElseIf .Shapes(ThirdFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(ThirdFormControlName).OLEFormat.Object.Caption
ElseIf .Shapes(FourthFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(FourthFormControlName).OLEFormat.Object.Caption
Else
ReturnFormControlCaption = "Not specified"
End If
ElseIf SixthFormControlName = "" Then
If .Shapes(FirstFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(FirstFormControlName).OLEFormat.Object.Caption
ElseIf .Shapes(SecondFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(SecondFormControlName).OLEFormat.Object.Caption
ElseIf .Shapes(ThirdFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(ThirdFormControlName).OLEFormat.Object.Caption
ElseIf .Shapes(FourthFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(FifthFormControlName).OLEFormat.Object.Caption
ElseIf .Shapes(FifthFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(FifthFormControlName).OLEFormat.Object.Caption
Else
ReturnFormControlCaption = "Not specified"
End If
Else
If .Shapes(FirstFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(FirstFormControlName).OLEFormat.Object.Caption
ElseIf .Shapes(SecondFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(SecondFormControlName).OLEFormat.Object.Caption
ElseIf .Shapes(ThirdFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(ThirdFormControlName).OLEFormat.Object.Caption
ElseIf .Shapes(FourthFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(FifthFormControlName).OLEFormat.Object.Caption
ElseIf .Shapes(FifthFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(FifthFormControlName).OLEFormat.Object.Caption
ElseIf .Shapes(SixthFormControlName).OLEFormat.Object.Value = 1 Then
ReturnFormControlCaption = .Shapes(SixthFormControlName).OLEFormat.Object.Caption
Else
ReturnFormControlCaption = "Not specified"
End If
End If
End With
End Function
To briefly explain the function, you pass string variables for the relevant worksheet name, and at least one (up to six) form control name(s).
The lengthy and nested If...ElseIf...Else statements are first establishing up to which argument has been included. Then depending on which argument is the first empty or "" value, it executes the next If...ElseIf...Else statement to determine in this case, which OptionButton is selected and then returns the .Caption of that OptionButton.
If none of the OptionButton being evaluated are selected, it returns "Not specified".
Note: This function will work for determining which CheckBox is checked BUT if in your passed arguments, more than one is selected, it will only return the .Caption of the first CheckBox that is checked. With some modification you could get the function working for both types including all CheckBox being checked.
Chip Pearson has some excellent information about Arrays and how to use them. You can read up on them on his website at www.cpearson.com or specifially what we have done here with arrays on this article on his website

VBA: matching multiple strings

MY question would best be understood be the following example, my goal is to classify the following string into category if the string matches any one of the strings defined in those categories. For example,
dim test_str as string
test_str = "tomato"
If the test string tomato matches any one of the keywords (1) potato, (2) tomato and (3) spaghetti, then tomato will be classified as food.
I have a very inefficient way of doing this now, which involves using multiple strcomp, i.e.
if(strcomp(test_str, "potato", vbtextcompare) = 0 or _
strcomp(test_str, "tomato", vbtextcompare) =0 or _
strcomp(test_str, "spaghetti", vbtextcompare)=0 ) then
'label test str as "food"
However, if I have 10 keywords defined within "food", I would then need 10 strcomp statements, which would be tedious. Is there a better way to do this ?
I would simply store all the combinations in a string and check that the value is present with InStr:
Const food = "|potato|tomato|spaghetti|"
Dim test_str As String
test_str = "tomato"
If InStr(1, food, "|" & test_str & "|", vbTextCompare) Then
Debug.Print "food"
Else
Debug.Print "not food"
End If
Write a function that helps you
Function ArrayWordNotInText(textValue, arrayKeyword)
Dim i
ArrayWordNotInText = -1
For i = LBound(arrayKeyword) To UBound(arrayKeyword)
If Not StrComp(textValue, arrayKeyword(i), vbTextCompare) Then ArrayWordNotInText = i
Next i
End Function
If the return value = -1 ... no Match, >0 the index of the word
This is my first time posting; excuse my formatting. Have not been using VBA for too long but was able to piece this together.
Sub vinden4()
Dim EXCEPT() As String, a As Integer
EM = "no.replynoreply#ziggo.nl"
Exceptions = "no-Reply,noreply,nO.reply,"
EXCEPT = Split(Exceptions, ",")
For i = LBound(EXCEPT) To UBound(EXCEPT)
NOREPLY = InStr(1, EM, EXCEPT(i), vbTextCompare)
If NOREPLY > 0 Then
'CbEM.Value = True '~food~
EM = InputBox("NOREPLY E-MAILADRES", "Geef E-mailadres aan", EM)
'else
'CbEM.Value = False ~not food~
End If
Next i
MsgBox EM
End Sub
Hope this can help someone.

Excel VBA Custom Function Remove Words Appearing in One String From Another String

I am trying to remove words appearing in one string from a different string using a custom function. For instance:
A1:
the was why blue hat
A2:
the stranger wanted to know why his blue hat was turning orange
The ideal outcome in this example would be:
A3:
stranger wanted to know his turning orange
I need to have the cells in reference open to change so that they can be used in different situations.
The function will be used in a cell as:
=WORDREMOVE("cell with words needing remove", "cell with list of words being removed")
I have a list of 20,000 rows and have managed to find a custom function that can remove duplicate words (below) and thought there may be a way to manipulate it to accomplish this task.
Function REMOVEDUPEWORDS(txt As String, Optional delim As String = " ") As String
Dim x
'Updateby20140924
With CreateObject("Scripting.Dictionary")
.CompareMode = vbTextCompare
For Each x In Split(txt, delim)
If Trim(x) <> "" And Not .exists(Trim(x)) Then .Add Trim(x), Nothing
Next
If .Count > 0 Then REMOVEDUPEWORDS = Join(.keys, delim)
End With
End Function
If you can guarantee that your words in both strings will be separated by spaces (no comma, ellipses, etc), you could just Split() both strings then Filter() out the words:
Function WORDREMOVE(ByVal strText As String, strRemove As String) As String
Dim a, w
a = Split(strText) ' Start with all words in an array
For Each w In Split(strRemove)
a = Filter(a, w, False, vbTextCompare) ' Remove every word found
Next
WORDREMOVE = Join(a, " ") ' Recreate the string
End Function
You can also do this using Regular Expressions in VBA. The version below is case insensitive and assumes all words are separated only by space. If there is other punctuation, more examples would aid in crafting an appropriate solution:
Option Explicit
Function WordRemove(Str As String, RemoveWords As String) As String
Dim RE As Object
Set RE = CreateObject("vbscript.regexp")
With RE
.ignorecase = True
.Global = True
.Pattern = "(?:" & Join(Split(WorksheetFunction.Trim(RemoveWords)), "|") & ")\s*"
WordRemove = .Replace(Str, "")
End With
End Function
My example is certainly not the best code, but it should work
Function WORDREMOVE(FirstCell As String, SecondCell As String)
Dim FirstArgument As Variant, SecondArgument As Variant
Dim FirstArgumentCounter As Integer, SecondArgumentCounter As Integer
Dim Checker As Boolean
WORDREMOVE = ""
FirstArgument = Split(FirstCell, " ")
SecondArgument = Split(SecondCell, " ")
For SecondArgumentCounter = 0 To UBound(SecondArgument)
Checker = False
For FirstArgumentCounter = 0 To UBound(FirstArgument)
If SecondArgument(SecondArgumentCounter) = FirstArgument(FirstArgumentCounter) Then
Checker = True
End If
Next FirstArgumentCounter
If Checker = False Then WORDREMOVE = WORDREMOVE & SecondArgument(SecondArgumentCounter) & " "
Next SecondArgumentCounter
WORDREMOVE = Left(WORDREMOVE, Len(WORDREMOVE) - 1)
End Function

Extracting last name from a range having suffixes using VBA

I have a list of full names in a column like for example:
Dave M. Butterworth
Dave M. Butterworth,II
H.F. Light jr
H.F. Light ,jr.
H.F. Light sr
Halle plumerey
The names are in a column. The initials are not limited to these only.
I want to extract the last name using a generic function. Is it possible?
Consider the following UDF:
Public Function LastName(sIn As String) As String
Dim Ka As Long, t As String
ary = Split(sIn, " ")
Ka = UBound(ary)
t = ary(Ka)
If t = "jr" Or t = ",jr" Or t = "sr" Or t = ",jr." Then
Ka = Ka - 1
End If
t = ary(Ka)
If InStr(1, t, ",") = 0 Then
LastName = t
Exit Function
End If
bry = Split(t, ",")
LastName = bry(LBound(bry))
End Function
NOTE:
You will have to expand this line:
If t = "jr" Or t = ",jr" Or t = "sr" Or t = ",jr." Then
to include all other initial sets you wish to exclude.You will also have to update this code to handle other exceptions as you find them !
Remove punctuation, split to an array and walk backwards until you find a string that does not match a lookup of ignorable monikers like "ii/jr/sr/dr".
You could also add a check to eliminate tokens based on their length.
Function LastName(name As String) As String
Dim parts() As String, i As Long
parts = Split(Trim$(Replace$(Replace$(name, ",", ""), ".", "")), " ")
For i = UBound(parts) To 0 Step -1
Select Case UCase$(parts(i))
Case "", "JR", "SR", "DR", "I", "II"
Case Else:
LastName = parts(i)
Exit Function
End Select
Next
End Function

Linq Dynamic Error with Dynamic Where clause

I have a four dropdownlists that are used to filter/order a gridview.
If any of the first 3 dropdowns have a selected value other than 0 the the WhereFlag is set to True. The fourth dropdown is used to dictate which column the grid is ordered by.
My code for databinding the gridview uses System.Linq.Dynamic and is shown below...
Dim dc As New DiaryDataContext
If WhereFlag = False Then
'This section works...
Dim AList = dc.D_AreaSubAreas _
.OrderBy(ddl_SortBy.SelectedValue)
Dim dl As List(Of D_AreaSubArea)
dl = AList.ToList
dl.Insert(0, New D_AreaSubArea With {.Ref = 0,
.Area = "",
.SubArea = "",
.Allocation = "Allocation...",
.Redundant = False})
gv_AreaSubArea.DataSource = dl
gv_AreaSubArea.DataBind()
'Gridview successfully binds
'If ddl_SortBy value is changed... Gridview binds OK.
Else
'This section gives error...
Dim WhereBuild As New StringBuilder
If ddl_AreaFilter.SelectedIndex <> 0 Then
WhereBuild.Append("Area = '" & ddl_AreaFilter.SelectedValue & "'")
AndFlag = True
End If
If ddl_SubAreaFilter.SelectedIndex <> 0 Then
If AndFlag = True Then
WhereBuild.Append(" AND ")
End If
WhereBuild.Append("SubArea = '" & ddl_SubAreaFilter.SelectedValue & "'")
AndFlag = True
End If
If ddl_AllocFilter.SelectedIndex <> 0 Then
If AndFlag = True Then
WhereBuild.Append(" AND ")
End If
WhereBuild.Append("Allocation = '" & ddl_AllocFilter.SelectedValue & "'")
End If
'ERROR HERE
Dim AList = dc.D_AreaSubAreas _
.Where(WhereBuild.ToString) _
.OrderBy(ddl_SortBy.SelectedValue)
'END ERROR
Dim dl As List(Of D_AreaSubArea)
dl = AList.ToList
dl.Insert(0, New D_AreaSubArea With {.Ref = 0,
.Area = "",
.SubArea = "",
.Allocation = "Allocation...",
.Redundant = False})
gv_AreaSubArea.DataSource = dl
gv_AreaSubArea.DataBind()
End If
The error I get is with the dynamic where clause. The error I get is
ParseException is unhandled by user code. Character Literal must contain exactly one character
It points to the query AList in the Else fork. The only difference between it and the query in the If fork is the addition of the Where clause... but I have been unable to deduce what is wrong with my code. AHA.
The error points to the place where the LINQ query is evaluated and, as the message says, the problem is that a character is expected but several characters were provided.
Check whenever ddl_AreaFilter.SelectedValue, ddl_SubAreaFilter.SelectedValue or ddl_AllocFilter.SelectedValue actually contain a character or a string. If they contain more than one character, you should replace ' with \" when building the where condition, for instance:
WhereBuild.Append("Area = """ & ddl_AreaFilter.SelectedValue & """")
EDIT
You must make sure that the type of the value contained in each SelectedValue string match the corresponding database type. For instance, if the database column is a numeric type, the string content will be casted to a numeric type.
When you specify the value in the comparison using quotes, you are indicating that the type on the right side of the comparison is either character or string (depending on whenever you use single or double quotes, respectively).
So, what are the Area, SubArea and Allocation types in the database?
Single character: the value in your query should be around single quotes: Area = 'value'
Strings (for instance, varchar):you must use double quotes: Area = "value"
Other: then you should use no quotes: Area = value